diff options
Diffstat (limited to 'src')
21 files changed, 266 insertions, 126 deletions
diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index ea94eb4e..c83b3670 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -120,7 +120,7 @@ public @interface Builder { @Retention(SOURCE) public @interface Default {} - /** @return Name of the method that creates a new builder instance. Default: {@code builder}. */ + /** @return Name of the method that creates a new builder instance. Default: {@code builder}. If the empty string, suppress generating the {@code builder} method. */ String builderMethodName() default "builder"; /** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java index b2fc672d..e752165c 100644 --- a/src/core/lombok/EqualsAndHashCode.java +++ b/src/core/lombok/EqualsAndHashCode.java @@ -97,6 +97,8 @@ public @interface EqualsAndHashCode { /** * Only include fields and methods explicitly marked with {@code @EqualsAndHashCode.Include}. * Normally, all (non-static, non-transient) fields are included by default. + * + * @return If {@code true}, don't include non-static non-transient fields automatically (default: {@code false}). */ boolean onlyExplicitlyIncluded() default false; @@ -113,7 +115,12 @@ public @interface EqualsAndHashCode { @Target({ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface Include { - /** Defaults to the method name of the annotated member. If on a method and the name equals the name of a default-included field, this member takes its place. */ + /** + * Defaults to the method name of the annotated member. + * If on a method and the name equals the name of a default-included field, this member takes its place. + * + * @return If present, this method serves as replacement for the named field. + */ String replaces() default ""; } } diff --git a/src/core/lombok/NonNull.java b/src/core/lombok/NonNull.java index caf6ed05..ba8c24a4 100644 --- a/src/core/lombok/NonNull.java +++ b/src/core/lombok/NonNull.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -30,16 +30,7 @@ import java.lang.annotation.Target; /** * 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 - * you, you should use your own annotation named {@code @NonNull} instead of this one. + * a value to this field will also produce these null-checks. */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE}) @Retention(RetentionPolicy.CLASS) diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java index ae3f071f..34418a2f 100644 --- a/src/core/lombok/ToString.java +++ b/src/core/lombok/ToString.java @@ -83,6 +83,8 @@ public @interface ToString { /** * Only include fields and methods explicitly marked with {@code @ToString.Include}. * Normally, all (non-static) fields are included by default. + * + * @return If {@code true}, don't include non-static fields automatically (default: {@code false}). */ boolean onlyExplicitlyIncluded() default false; @@ -102,10 +104,19 @@ public @interface ToString { // /** If true and the return value is {@code null}, omit this member entirely from the {@code toString} output. */ // boolean skipNull() default false; // -- We'll add it later, it requires a complete rework on the toString code we generate. - /** Higher ranks are printed first. Members of the same rank are printed in the order they appear in the source file. */ + /** + * Higher ranks are printed first. Members of the same rank are printed in the order they appear in the source file. + * + * @return ordering within the generating {@code toString()}; higher numbers are printed first. + */ int rank() default 0; - /** Defaults to the field / method name of the annotated member. If the name equals the name of a default-included field, this member takes its place. */ + /** + * Defaults to the field / method name of the annotated member. + * If the name equals the name of a default-included field, this member takes its place. + * + * @return The name to show in the generated {@code toString()}. Also, if this annotation is on a method and the name matches an existing field, it replaces that field. + */ String name() default ""; } } diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java index 78761f46..9f3a471f 100755 --- a/src/core/lombok/core/AST.java +++ b/src/core/lombok/core/AST.java @@ -60,6 +60,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, private final String fileName; private final String packageDeclaration; private final ImportList imports; + private TypeResolver importsAsResolver; Map<N, N> identityDetector = new IdentityHashMap<N, N>(); private Map<N, L> nodeMap = new IdentityHashMap<N, L>(); private boolean changed = false; @@ -112,14 +113,20 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, /** * Return the contents of each non-static import statement on this AST's top (Compilation Unit) node. - * - * Example: "java.util.IOException". */ public final ImportList getImportList() { return imports; } /** + * Return the contents of each non-static import statement on this AST's top (Compilation Unit) node, packed into a (cached) TypeResolver. + */ + public final TypeResolver getImportListAsTypeResolver() { + if (importsAsResolver != null) return importsAsResolver; + return importsAsResolver = new TypeResolver(getImportList()); + } + + /** * Puts the given node in the map so that javac/Eclipse's own internal AST object can be translated to * an AST.Node object. Also registers the object as visited to avoid endless loops. */ diff --git a/src/core/lombok/core/CleanupRegistry.java b/src/core/lombok/core/CleanupRegistry.java index 82a52fdb..d6aee477 100644 --- a/src/core/lombok/core/CleanupRegistry.java +++ b/src/core/lombok/core/CleanupRegistry.java @@ -21,8 +21,8 @@ */ package lombok.core; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; public class CleanupRegistry { private static final class CleanupKey { @@ -48,7 +48,7 @@ public class CleanupRegistry { } } - private final Map<CleanupKey, CleanupTask> tasks = new ConcurrentHashMap<CleanupKey, CleanupTask>(); + private final ConcurrentMap<CleanupKey, CleanupTask> tasks = new ConcurrentHashMap<CleanupKey, CleanupTask>(); public void registerTask(String key, Object target, CleanupTask task) { CleanupKey ck = new CleanupKey(key, target); diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java index ae8fd325..843a78f0 100644 --- a/src/core/lombok/core/LombokNode.java +++ b/src/core/lombok/core/LombokNode.java @@ -97,6 +97,15 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, } /** + * Convenient shortcut to the owning ast object's {@code getImportListAsTypeResolver} method. + * + * @see AST#getImportListAsTypeResolver() + */ + public TypeResolver getImportListAsTypeResolver() { + return getAst().getImportListAsTypeResolver(); + } + + /** * See {@link #isStructurallySignificant}. */ protected abstract boolean calculateIsStructurallySignificant(N parent); diff --git a/src/core/lombok/core/configuration/NullCheckExceptionType.java b/src/core/lombok/core/configuration/NullCheckExceptionType.java index c4bb71f2..d226c0a8 100644 --- a/src/core/lombok/core/configuration/NullCheckExceptionType.java +++ b/src/core/lombok/core/configuration/NullCheckExceptionType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2018 The Project Lombok Authors. + * Copyright (C) 2014-2019 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 @@ -22,27 +22,27 @@ package lombok.core.configuration; -@ExampleValueString("[NullPointerException | IllegalArgumentException]") +@ExampleValueString("[NullPointerException | IllegalArgumentException | Assertion]") public enum NullCheckExceptionType { ILLEGAL_ARGUMENT_EXCEPTION { - public String toExceptionMessage(String fieldName) { - return fieldName + " is marked @NonNull but is null"; - } - @Override public String getExceptionType() { return "java.lang.IllegalArgumentException"; } }, NULL_POINTER_EXCEPTION { - @Override public String toExceptionMessage(String fieldName) { - return fieldName + " is marked @NonNull but is null"; - } - - public String getExceptionType() { + @Override public String getExceptionType() { return "java.lang.NullPointerException"; } + }, + ASSERTION { + @Override public String getExceptionType() { + return null; + } }; - public abstract String toExceptionMessage(String fieldName); + public String toExceptionMessage(String fieldName) { + return fieldName + " is marked non-null but is null"; + } + public abstract String getExceptionType(); } diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index d0092b53..64d17cd9 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -84,8 +84,7 @@ public class HandlerUtil { "com.sun.istack.internal.NotNull", "edu.umd.cs.findbugs.annotations.NonNull", "javax.annotation.Nonnull", - // The field might contain a null value until it is persisted. - // "javax.validation.constraints.NotNull", + // "javax.validation.constraints.NotNull", // The field might contain a null value until it is persisted. "lombok.NonNull", "org.checkerframework.checker.nullness.qual.NonNull", "org.eclipse.jdt.annotation.NonNull", @@ -105,8 +104,8 @@ public class HandlerUtil { "javax.annotation.Nonnull", "javax.annotation.Nullable", "lombok.NonNull", - // To update Checker Framework annotations, run: - // grep --recursive --files-with-matches -e '^@Target\b.*TYPE_USE' $CHECKERFRAMEWORK/checker/src/main/java $CHECKERFRAMEWORK/framework/src/main/java | grep '\.java$' | sed 's/.*\/java\//\t\t\t"/' | sed 's/\.java$/",/' | sed 's/\//./g' | sort + // To update Checker Framework annotations, run: + // grep --recursive --files-with-matches -e '^@Target\b.*TYPE_USE' $CHECKERFRAMEWORK/checker/src/main/java $CHECKERFRAMEWORK/framework/src/main/java | grep '\.java$' | sed 's/.*\/java\//\t\t\t"/' | sed 's/\.java$/",/' | sed 's/\//./g' | sort "org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey", "org.checkerframework.checker.compilermsgs.qual.CompilerMessageKeyBottom", "org.checkerframework.checker.compilermsgs.qual.UnknownCompilerMessageKey", @@ -293,6 +292,7 @@ public class HandlerUtil { "org.checkerframework.common.value.qual.UnknownVal", "org.checkerframework.framework.qual.PolyAll", "org.checkerframework.framework.util.PurityUnqualified", + "org.eclipse.jdt.annotation.NonNull", "org.eclipse.jdt.annotation.Nullable", "org.jetbrains.annotations.NotNull", diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 9439caf3..010dc9d8 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -66,6 +66,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.AssertStatement; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.CastExpression; import org.eclipse.jdt.internal.compiler.ast.CharLiteral; @@ -221,14 +222,14 @@ public class EclipseHandlerUtil { * @param typeRef A type reference to check. */ public static boolean typeMatches(String type, EclipseNode node, TypeReference typeRef) { - if (typeRef == null || typeRef.getTypeName() == null || typeRef.getTypeName().length == 0) return false; - String lastPartA = new String(typeRef.getTypeName()[typeRef.getTypeName().length -1]); - int lastIndex = type.lastIndexOf('.'); - String lastPartB = lastIndex == -1 ? type : type.substring(lastIndex + 1); - if (!lastPartA.equals(lastPartB)) return false; - String typeName = toQualifiedName(typeRef.getTypeName()); - - TypeResolver resolver = new TypeResolver(node.getImportList()); + char[][] tn = typeRef == null ? null : typeRef.getTypeName(); + if (tn == null || tn.length == 0) return false; + char[] lastPartA = tn[tn.length - 1]; + int lastIndex = type.lastIndexOf('.') + 1; + if (lastPartA.length != type.length() - lastIndex) return false; + for (int i = 0; i < lastPartA.length; i++) if (lastPartA[i] != type.charAt(i + lastIndex)) return false; + String typeName = toQualifiedName(tn); + TypeResolver resolver = node.getImportListAsTypeResolver(); return resolver.typeMatches(node, type, typeName); } @@ -1786,27 +1787,39 @@ public class EclipseHandlerUtil { if (isPrimitive(variable.type)) return null; AllocationExpression exception = new AllocationExpression(); setGeneratedBy(exception, source); - int partCount = 1; + + SingleNameReference varName = new SingleNameReference(variable.name, p); + setGeneratedBy(varName, source); + NullLiteral nullLiteral = new NullLiteral(pS, pE); + setGeneratedBy(nullLiteral, source); + + int equalOperator = exceptionType == NullCheckExceptionType.ASSERTION ? OperatorIds.NOT_EQUAL : OperatorIds.EQUAL_EQUAL; + EqualExpression equalExpression = new EqualExpression(varName, nullLiteral, equalOperator); + equalExpression.sourceStart = pS; equalExpression.statementEnd = equalExpression.sourceEnd = pE; + setGeneratedBy(equalExpression, source); + + StringLiteral message = new StringLiteral(exceptionType.toExceptionMessage(new String(variable.name)).toCharArray(), pS, pE, 0); + setGeneratedBy(message, source); + + if (exceptionType == NullCheckExceptionType.ASSERTION) { + Statement assertStatement = new AssertStatement(message, equalExpression, pS); + setGeneratedBy(assertStatement, source); + return assertStatement; + } + String exceptionTypeStr = exceptionType.getExceptionType(); + int partCount = 1; for (int i = 0; i < exceptionTypeStr.length(); i++) if (exceptionTypeStr.charAt(i) == '.') partCount++; long[] ps = new long[partCount]; Arrays.fill(ps, 0L); exception.type = new QualifiedTypeReference(fromQualifiedName(exceptionTypeStr), ps); setGeneratedBy(exception.type, source); - exception.arguments = new Expression[] { - new StringLiteral(exceptionType.toExceptionMessage(new String(variable.name)).toCharArray(), pS, pE, 0) - }; - setGeneratedBy(exception.arguments[0], source); + exception.arguments = new Expression[] {message}; + ThrowStatement throwStatement = new ThrowStatement(exception, pS, pE); setGeneratedBy(throwStatement, source); - SingleNameReference varName = new SingleNameReference(variable.name, p); - setGeneratedBy(varName, source); - NullLiteral nullLiteral = new NullLiteral(pS, pE); - setGeneratedBy(nullLiteral, source); - EqualExpression equalExpression = new EqualExpression(varName, nullLiteral, OperatorIds.EQUAL_EQUAL); - equalExpression.sourceStart = pS; equalExpression.statementEnd = equalExpression.sourceEnd = pE; - setGeneratedBy(equalExpression, source); + Block throwBlock = new Block(0); throwBlock.statements = new Statement[] {throwStatement}; throwBlock.sourceStart = pS; throwBlock.sourceEnd = pE; diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 3391b99d..7a831f3d 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -170,10 +170,18 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { List<char[]> typeArgsForToBuilder = null; if (builderMethodName == null) builderMethodName = "builder"; - if (buildMethodName == null) builderMethodName = "build"; + if (buildMethodName == null) buildMethodName = "build"; if (builderClassName == null) builderClassName = ""; - if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; + boolean generateBuilderMethod; + if (builderMethodName.isEmpty()) { + generateBuilderMethod = false; + } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + return; + } else { + generateBuilderMethod = true; + } + if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; if (!builderClassName.isEmpty()) { if (!checkName("builderClassName", builderClassName, annotationNode)) return; @@ -192,6 +200,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { boolean addCleaning = false; boolean isStatic = true; + List<EclipseNode> nonFinalNonDefaultedFields = null; + if (parent.get() instanceof TypeDeclaration) { tdParent = parent; TypeDeclaration td = (TypeDeclaration) tdParent.get(); @@ -225,7 +235,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (fd.initialization != null && isDefault == null) { if (isFinal) continue; - fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<EclipseNode>(); + nonFinalNonDefaultedFields.add(fieldNode); } if (isDefault != null) { @@ -486,7 +497,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (cleanMethod != null) injectMethod(builderType, cleanMethod); } - if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { + if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod) { MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast); if (md != null) injectMethod(tdParent, md); } @@ -508,6 +520,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (md != null) injectMethod(tdParent, md); } + + if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { + for (EclipseNode fieldNode : nonFinalNonDefaultedFields) { + fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + } } private static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'}; diff --git a/src/core/lombok/eclipse/handlers/HandleNonNull.java b/src/core/lombok/eclipse/handlers/HandleNonNull.java index 1672618d..77c77e1e 100644 --- a/src/core/lombok/eclipse/handlers/HandleNonNull.java +++ b/src/core/lombok/eclipse/handlers/HandleNonNull.java @@ -21,27 +21,18 @@ */ package lombok.eclipse.handlers; -import static lombok.core.handlers.HandlerUtil.*; +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; import static lombok.eclipse.Eclipse.isPrimitive; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.Arrays; -import lombok.ConfigurationKeys; -import lombok.NonNull; -import lombok.core.AST.Kind; -import lombok.core.AnnotationValues; -import lombok.core.HandlerPriority; -import lombok.eclipse.DeferUntilPostDiet; -import lombok.eclipse.EclipseAST; -import lombok.eclipse.EclipseAnnotationHandler; -import lombok.eclipse.EclipseNode; - 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.AssertStatement; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; @@ -56,6 +47,16 @@ import org.eclipse.jdt.internal.compiler.ast.TryStatement; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.mangosdk.spi.ProviderFor; +import lombok.ConfigurationKeys; +import lombok.NonNull; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.eclipse.DeferUntilPostDiet; +import lombok.eclipse.EclipseAST; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; + @DeferUntilPostDiet @ProviderFor(EclipseAnnotationHandler.class) @HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. @@ -191,9 +192,11 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { } public char[] returnVarNameIfNullCheck(Statement stat) { - if (!(stat instanceof IfStatement)) return null; + boolean isIf = stat instanceof IfStatement; + if (!isIf && !(stat instanceof AssertStatement)) return null; - /* Check that the if's statement is a throw statement, possibly in a block. */ { + if (isIf) { + /* 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; @@ -206,11 +209,15 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { /* 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; + Expression cond = isIf ? ((IfStatement) stat).condition : ((AssertStatement) stat).assertExpression; 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 (isIf) { + if (operatorId != OperatorIds.EQUAL_EQUAL) return null; + } else { + if (operatorId != OperatorIds.NOT_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/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 7b6a3c28..6f34eb30 100755 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -129,7 +129,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; - if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; + boolean generateBuilderMethod; + if (builderMethodName.isEmpty()) { + generateBuilderMethod = false; + } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + return; + } else { + generateBuilderMethod = true; + } if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; boolean toBuilder = superbuilderAnnotation.toBuilder(); @@ -150,6 +157,8 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { // Gather all fields of the class that should be set by the builder. List<EclipseNode> allFields = new ArrayList<EclipseNode>(); + List<EclipseNode> nonFinalNonDefaultedFields = null; + boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); @@ -177,10 +186,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } if (fd.initialization != null && isDefault == null) { - if (isFinal) { - continue; - } - fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + if (isFinal) continue; + if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<EclipseNode>(); + nonFinalNonDefaultedFields.add(fieldNode); } if (isDefault != null) { @@ -386,10 +394,17 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { } // Add the builder() method to the annotated class. - if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { + if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod) { MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast); if (md != null) injectMethod(tdParent, md); } + + if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { + for (EclipseNode fieldNode : nonFinalNonDefaultedFields) { + fieldNode.addWarning("@SuperBuilder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + } } private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass, diff --git a/src/core/lombok/experimental/FieldNameConstants.java b/src/core/lombok/experimental/FieldNameConstants.java index d6886890..da97a721 100644 --- a/src/core/lombok/experimental/FieldNameConstants.java +++ b/src/core/lombok/experimental/FieldNameConstants.java @@ -41,6 +41,8 @@ public @interface FieldNameConstants { /** * Only include fields and methods explicitly marked with {@code @FieldNameConstants.Include}. * Normally, all (non-static) fields are included by default. + * + * @return If {@code true}, don't include non-static fields automatically (default: {@code false}). */ boolean onlyExplicitlyIncluded() default false; diff --git a/src/core/lombok/experimental/SuperBuilder.java b/src/core/lombok/experimental/SuperBuilder.java index be6ea304..aef76a46 100644 --- a/src/core/lombok/experimental/SuperBuilder.java +++ b/src/core/lombok/experimental/SuperBuilder.java @@ -49,7 +49,7 @@ import lombok.Singular; @Target(TYPE) @Retention(SOURCE) public @interface SuperBuilder { - /** @return Name of the method that creates a new builder instance. Default: {@code builder}. */ + /** @return Name of the method that creates a new builder instance. Default: {@code builder}. If the empty string, suppress generating the {@code builder} method. */ String builderMethodName() default "builder"; /** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 609adbd6..d6fb9728 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -119,7 +119,15 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (buildMethodName == null) buildMethodName = "build"; if (builderClassName == null) builderClassName = ""; - if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; + boolean generateBuilderMethod; + if (builderMethodName.isEmpty()) { + generateBuilderMethod = false; + } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + return; + } else { + generateBuilderMethod = true; + } + if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; if (!builderClassName.isEmpty()) { if (!checkName("builderClassName", builderClassName, annotationNode)) return; @@ -140,6 +148,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { boolean addCleaning = false; boolean isStatic = true; + ArrayList<JavacNode> nonFinalNonDefaultedFields = null; + if (parent.get() instanceof JCClassDecl) { tdParent = parent; JCClassDecl td = (JCClassDecl) tdParent.get(); @@ -172,7 +182,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (fd.init != null && isDefault == null) { if (isFinal) continue; - fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<JavacNode>(); + nonFinalNonDefaultedFields.add(fieldNode); } if (isDefault != null) { @@ -406,7 +417,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } { - MemberExistsResult methodExists = methodExists(builderMethodName, builderType, -1); + MemberExistsResult methodExists = methodExists(buildMethodName, builderType, -1); if (methodExists == MemberExistsResult.EXISTS_BY_LOMBOK) methodExists = methodExists(buildMethodName, builderType, 0); if (methodExists == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning); @@ -431,7 +442,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); - if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { + if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod) { JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams); recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); if (md != null) injectMethod(tdParent, md); @@ -459,6 +471,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } } } + + if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { + for (JavacNode fieldNode : nonFinalNonDefaultedFields) { + fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + } } private static String unpack(JCExpression expr) { diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java index 9a81ffff..49b987ce 100644 --- a/src/core/lombok/javac/handlers/HandleNonNull.java +++ b/src/core/lombok/javac/handlers/HandleNonNull.java @@ -21,13 +21,16 @@ */ package lombok.javac.handlers; -import static lombok.core.handlers.HandlerUtil.*; +import static lombok.core.handlers.HandlerUtil.handleFlagUsage; import static lombok.javac.Javac.*; +import static lombok.javac.JavacTreeMaker.TreeTag.treeTag; +import static lombok.javac.JavacTreeMaker.TypeTag.typeTag; 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.JCAssert; import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCExpression; @@ -45,13 +48,11 @@ import com.sun.tools.javac.util.List; import lombok.ConfigurationKeys; import lombok.NonNull; +import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.core.AST.Kind; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; -import static lombok.javac.JavacTreeMaker.TypeTag.*; -import static lombok.javac.JavacTreeMaker.TreeTag.*; @ProviderFor(JavacAnnotationHandler.class) @HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. @@ -161,14 +162,16 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { } /** - * Checks if the statement is of the form 'if (x == null) {throw WHATEVER;}, + * Checks if the statement is of the form 'if (x == null) {throw WHATEVER;}' or 'assert x != null;', * where the block braces are optional. If it is of this form, returns "x". * If it is not of this form, returns null. */ public String returnVarNameIfNullCheck(JCStatement stat) { - if (!(stat instanceof JCIf)) return null; + boolean isIf = stat instanceof JCIf; + if (!isIf && !(stat instanceof JCAssert)) return null; - /* Check that the if's statement is a throw statement, possibly in a block. */ { + if (isIf) { + /* 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; @@ -180,11 +183,15 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> { /* 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; + JCExpression cond = isIf ? ((JCIf) stat).cond : ((JCAssert) stat).cond; while (cond instanceof JCParens) cond = ((JCParens) cond).expr; if (!(cond instanceof JCBinary)) return null; JCBinary bin = (JCBinary) cond; - if (!CTC_EQUAL.equals(treeTag(bin))) return null; + if (isIf) { + if (!CTC_EQUAL.equals(treeTag(bin))) return null; + } else { + if (!CTC_NOT_EQUAL.equals(treeTag(bin))) return null; + } if (!(bin.lhs instanceof JCIdent)) return null; if (!(bin.rhs instanceof JCLiteral)) return null; if (!CTC_BOT.equals(typeTag(bin.rhs))) return null; diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 7ef6b658..7eb9873b 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -103,7 +103,14 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; - if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; + boolean generateBuilderMethod; + if (builderMethodName.isEmpty()) { + generateBuilderMethod = false; + } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { + return; + } else { + generateBuilderMethod = true; + } if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; boolean toBuilder = superbuilderAnnotation.toBuilder(); @@ -125,6 +132,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Gather all fields of the class that should be set by the builder. JCClassDecl td = (JCClassDecl) tdParent.get(); ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); + ArrayList<JavacNode> nonFinalNonDefaultedFields = null; + boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); @@ -150,7 +159,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { if (fd.init != null && isDefault == null) { if (isFinal) continue; - fieldNode.addWarning("@SuperBuilder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<JavacNode>(); + nonFinalNonDefaultedFields.add(fieldNode); } if (isDefault != null) { @@ -347,7 +357,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Add the builder() method to the annotated class. // Allow users to specify their own builder() methods, e.g., to provide default values. - if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { + if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; + if (generateBuilderMethod) { JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams); recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext()); if (builderMethod != null) injectMethod(tdParent, builderMethod); @@ -369,6 +380,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { // Should not happen. } } + + if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { + for (JavacNode fieldNode : nonFinalNonDefaultedFields) { + fieldNode.addWarning("@SuperBuilder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + } } /** diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index f08098d2..509a7397 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -40,36 +40,15 @@ import java.util.regex.Pattern; import javax.lang.model.element.Element; -import lombok.AccessLevel; -import lombok.ConfigurationKeys; -import lombok.Data; -import lombok.Getter; -import lombok.core.AST.Kind; -import lombok.core.AnnotationValues; -import lombok.core.LombokImmutableList; -import lombok.core.AnnotationValues.AnnotationValue; -import lombok.core.CleanupTask; -import lombok.core.TypeResolver; -import lombok.core.configuration.NullCheckExceptionType; -import lombok.core.configuration.TypeName; -import lombok.core.handlers.HandlerUtil; -import lombok.delombok.LombokOptionsFactory; -import lombok.experimental.Accessors; -import lombok.experimental.Tolerate; -import lombok.javac.Javac; -import lombok.javac.JavacNode; -import lombok.javac.JavacTreeMaker; -import lombok.permit.Permit; - import com.sun.tools.javac.code.BoundKind; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Scope; import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.code.Symtab; -import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; @@ -83,6 +62,7 @@ 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; +import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; @@ -103,6 +83,28 @@ import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Options; +import lombok.AccessLevel; +import lombok.ConfigurationKeys; +import lombok.Data; +import lombok.Getter; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.AnnotationValues.AnnotationValue; +import lombok.core.CleanupTask; +import lombok.core.LombokImmutableList; +import lombok.core.TypeResolver; +import lombok.core.configuration.NullCheckExceptionType; +import lombok.core.configuration.TypeName; +import lombok.core.handlers.HandlerUtil; +import lombok.core.handlers.HandlerUtil.FieldAccess; +import lombok.delombok.LombokOptionsFactory; +import lombok.experimental.Accessors; +import lombok.experimental.Tolerate; +import lombok.javac.Javac; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.permit.Permit; + /** * Container for static utility methods useful to handlers written for javac. */ @@ -265,7 +267,7 @@ public class JavacHandlerUtil { */ public static boolean annotationTypeMatches(Class<? extends Annotation> type, JavacNode node) { if (node.getKind() != Kind.ANNOTATION) return false; - return typeMatches(type, node, ((JCAnnotation)node.get()).annotationType); + return typeMatches(type, node, ((JCAnnotation) node.get()).annotationType); } /** @@ -298,9 +300,14 @@ public class JavacHandlerUtil { * @param typeNode A type reference to check. */ public static boolean typeMatches(String type, JavacNode node, JCTree typeNode) { - String typeName = typeNode.toString(); - - TypeResolver resolver = new TypeResolver(node.getImportList()); + String typeName = typeNode == null ? null : typeNode.toString(); + if (typeName == null || typeName.length() == 0) return false; + int lastIndexA = typeName.lastIndexOf('.') + 1; + int lastIndexB = Math.max(type.lastIndexOf('.'), type.lastIndexOf('$')) + 1; + int len = typeName.length() - lastIndexA; + if (len != type.length() - lastIndexB) return false; + for (int i = 0; i < len; i++) if (typeName.charAt(i + lastIndexA) != type.charAt(i + lastIndexB)) return false; + TypeResolver resolver = node.getImportListAsTypeResolver(); return resolver.typeMatches(node, type, typeName); } @@ -1479,8 +1486,14 @@ public class JavacHandlerUtil { if (isPrimitive(varDecl.vartype)) return null; Name fieldName = varDecl.name; + + JCLiteral message = maker.Literal(exceptionType.toExceptionMessage(fieldName.toString())); + if (exceptionType == NullCheckExceptionType.ASSERTION) { + return maker.Assert(maker.Binary(CTC_NOT_EQUAL, maker.Ident(fieldName), maker.Literal(CTC_BOT, null)), message); + } + JCExpression exType = genTypeRef(variable, exceptionType.getExceptionType()); - JCExpression exception = maker.NewClass(null, List.<JCExpression>nil(), exType, List.<JCExpression>of(maker.Literal(exceptionType.toExceptionMessage(fieldName.toString()))), null); + JCExpression exception = maker.NewClass(null, List.<JCExpression>nil(), exType, List.<JCExpression>of(message), null); JCStatement throwStatement = maker.Throw(exception); JCBlock throwBlock = maker.Block(0, List.of(throwStatement)); return maker.If(maker.Binary(CTC_EQUAL, maker.Ident(fieldName), maker.Literal(CTC_BOT, null)), throwBlock, null); diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java index 9582c3b8..8f4f99e5 100755 --- a/src/delombok/lombok/delombok/Delombok.java +++ b/src/delombok/lombok/delombok/Delombok.java @@ -753,6 +753,7 @@ public class Delombok { Object care = callAttributeMethodOnJavaCompiler(delegate, delegate.todo); callFlowMethodOnJavaCompiler(delegate, care); + FormatPreferences fps = new FormatPreferences(formatPrefs); for (JCCompilationUnit unit : roots) { DelombokResult result = new DelombokResult(catcher.getComments(unit), unit, force || options.isChanged(unit), fps); diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index 353bd753..832dbe0a 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -648,7 +648,11 @@ public class PrettyPrinter extends JCTree.Visitor { */ try { innermostArrayBracketsAreVarargs = varargs; - print(tree.vartype); + if (tree.vartype == null || tree.vartype.pos == -1) { + print("var"); + } else { + print(tree.vartype); + } } finally { innermostArrayBracketsAreVarargs = false; } |