diff options
4 files changed, 221 insertions, 32 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index cab847e6..6c7b4caf 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2017 The Project Lombok Authors. + * Copyright (C) 2010-2018 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 @@ -64,6 +64,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.FloatLiteral; import org.eclipse.jdt.internal.compiler.ast.IntLiteral; import org.eclipse.jdt.internal.compiler.ast.LongLiteral; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; @@ -96,10 +97,9 @@ public class HandleConstructor { boolean force = ann.force(); - List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList(); List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode); - handleConstructor.generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); + handleConstructor.generateConstructor(typeNode, level, Collections.<EclipseNode>emptyList(), force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); } } @@ -132,10 +132,6 @@ public class HandleConstructor { return findFields(typeNode, true); } - private static List<EclipseNode> findFinalFields(EclipseNode typeNode) { - return findFields(typeNode, false); - } - private static List<EclipseNode> findFields(EclipseNode typeNode, boolean nullMarked) { List<EclipseNode> fields = new ArrayList<EclipseNode>(); for (EclipseNode child : typeNode.down()) { @@ -215,9 +211,8 @@ public class HandleConstructor { Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.NO_ARGS_CONSTRUCTOR_EXTRA_PRIVATE); if (v == null || !v) return; - - List<EclipseNode> fields = findFinalFields(typeNode); - generate(typeNode, AccessLevel.PRIVATE, fields, true, null, SkipIfConstructorExists.NO, Collections.<Annotation>emptyList(), sourceNode, true); + + generate(typeNode, AccessLevel.PRIVATE, Collections.<EclipseNode>emptyList(), true, null, SkipIfConstructorExists.NO, Collections.<Annotation>emptyList(), sourceNode, true); } public void generateRequiredArgsConstructor( @@ -235,14 +230,14 @@ public class HandleConstructor { } public void generateConstructor( - EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, + EclipseNode typeNode, AccessLevel level, List<EclipseNode> fieldsToParam, boolean forceDefaults, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, EclipseNode sourceNode) { - generate(typeNode, level, fields, allToDefault, staticName, skipIfConstructorExists, onConstructor, sourceNode, false); + generate(typeNode, level, fieldsToParam, forceDefaults, staticName, skipIfConstructorExists, onConstructor, sourceNode, false); } public void generate( - EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, + EclipseNode typeNode, AccessLevel level, List<EclipseNode> fieldsToParam, boolean forceDefaults, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, EclipseNode sourceNode, boolean noArgs) { ASTNode source = sourceNode.get(); @@ -279,11 +274,11 @@ public class HandleConstructor { if (noArgs && noArgsConstructorExists(typeNode)) return; ConstructorDeclaration constr = createConstructor( - staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault, + staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fieldsToParam, forceDefaults, sourceNode, onConstructor); injectMethod(typeNode, constr); if (staticConstrRequired) { - MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.<EclipseNode>emptyList() : fields, source); + MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, fieldsToParam, source); injectMethod(typeNode, staticConstr); } } @@ -342,8 +337,16 @@ public class HandleConstructor { return new Annotation[] { ann }; } + private static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'}; + private static final char[] prefixWith(char[] prefix, char[] name) { + char[] out = new char[prefix.length + name.length]; + System.arraycopy(prefix, 0, out, 0, prefix.length); + System.arraycopy(name, 0, out, prefix.length, name.length); + return out; + } + @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor( - AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault, + AccessLevel level, EclipseNode type, Collection<EclipseNode> fieldsToParam, boolean forceDefaults, EclipseNode sourceNode, List<Annotation> onConstructor) { ASTNode source = sourceNode.get(); @@ -354,8 +357,11 @@ public class HandleConstructor { if (isEnum) level = AccessLevel.PRIVATE; + List<EclipseNode> fieldsToDefault = fieldsNeedingBuilderDefaults(type, fieldsToParam); + List<EclipseNode> fieldsToExplicit = forceDefaults ? fieldsNeedingExplicitDefaults(type, fieldsToParam) : Collections.<EclipseNode>emptyList(); + boolean addConstructorProperties; - if (fields.isEmpty()) { + if (fieldsToParam.isEmpty()) { addConstructorProperties = false; } else { Boolean v = type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES); @@ -381,7 +387,7 @@ public class HandleConstructor { List<Statement> assigns = new ArrayList<Statement>(); List<Statement> nullChecks = new ArrayList<Statement>(); - for (EclipseNode fieldNode : fields) { + for (EclipseNode fieldNode : fieldsToParam) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); char[] rawName = field.name; char[] fieldName = removePrefixFromField(fieldNode); @@ -390,23 +396,55 @@ public class HandleConstructor { int e = (int) p; thisX.receiver = new ThisReference(s, e); - Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p); + Expression assignmentExpr = new SingleNameReference(fieldName, p); Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32); assigns.add(assignment); - if (!allToDefault) { - long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd; - Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL); - Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); - Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); - if (nonNulls.length != 0) { - Statement nullCheck = generateNullCheck(parameter, sourceNode); - if (nullCheck != null) nullChecks.add(nullCheck); - } - parameter.annotations = copyAnnotations(source, nonNulls, nullables); - params.add(parameter); + long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd; + Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL); + Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); + Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); + if (nonNulls.length != 0) { + Statement nullCheck = generateNullCheck(parameter, sourceNode); + if (nullCheck != null) nullChecks.add(nullCheck); } + parameter.annotations = copyAnnotations(source, nonNulls, nullables); + params.add(parameter); + } + + for (EclipseNode fieldNode : fieldsToExplicit) { + FieldDeclaration field = (FieldDeclaration) fieldNode.get(); + char[] rawName = field.name; + FieldReference thisX = new FieldReference(rawName, p); + int s = (int) (p >> 32); + int e = (int) p; + thisX.receiver = new ThisReference(s, e); + + Expression assignmentExpr = getDefaultExpr(field.type, s, e); + + Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); + assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32); + assigns.add(assignment); + } + + for (EclipseNode fieldNode : fieldsToDefault) { + FieldDeclaration field = (FieldDeclaration) fieldNode.get(); + char[] rawName = field.name; + FieldReference thisX = new FieldReference(rawName, p); + int s = (int) (p >> 32); + int e = (int) p; + thisX.receiver = new ThisReference(s, e); + + MessageSend inv = new MessageSend(); + inv.sourceStart = source.sourceStart; + inv.sourceEnd = source.sourceEnd; + inv.receiver = new SingleNameReference(((TypeDeclaration) type.get()).name, 0L); + inv.selector = prefixWith(DEFAULT_PREFIX, removePrefixFromField(fieldNode)); + + Assignment assignment = new Assignment(thisX, inv, (int) p); + assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32); + assigns.add(assignment); } nullChecks.addAll(assigns); @@ -415,8 +453,8 @@ public class HandleConstructor { /* Generate annotations that must be put on the generated method, and attach them. */ { Annotation[] constructorProperties = null; - if (!allToDefault && addConstructorProperties && !isLocalType(type)) { - constructorProperties = createConstructorProperties(source, fields); + if (addConstructorProperties && !isLocalType(type)) { + constructorProperties = createConstructorProperties(source, fieldsToParam); } constructor.annotations = copyAnnotations(source, @@ -428,6 +466,34 @@ public class HandleConstructor { return constructor; } + private static List<EclipseNode> fieldsNeedingBuilderDefaults(EclipseNode type, Collection<EclipseNode> fieldsToParam) { + List<EclipseNode> out = new ArrayList<EclipseNode>(); + top: + for (EclipseNode node : type.down()) { + if (node.getKind() != Kind.FIELD) continue top; + FieldDeclaration fd = (FieldDeclaration) node.get(); + if ((fd.modifiers & ClassFileConstants.AccStatic) != 0) continue top; + for (EclipseNode ftp : fieldsToParam) if (node == ftp) continue top; + if (EclipseHandlerUtil.hasAnnotation(Builder.Default.class, node)) out.add(node); + } + return out; + } + + private static List<EclipseNode> fieldsNeedingExplicitDefaults(EclipseNode type, Collection<EclipseNode> fieldsToParam) { + List<EclipseNode> out = new ArrayList<EclipseNode>(); + top: + for (EclipseNode node : type.down()) { + if (node.getKind() != Kind.FIELD) continue top; + FieldDeclaration fd = (FieldDeclaration) node.get(); + if ((fd.modifiers & ClassFileConstants.AccFinal) == 0) continue top; + if ((fd.modifiers & ClassFileConstants.AccStatic) != 0) continue top; + for (EclipseNode ftp : fieldsToParam) if (node == ftp) continue top; + if (EclipseHandlerUtil.hasAnnotation(Builder.Default.class, node)) continue top; + out.add(node); + } + return out; + } + private static Expression getDefaultExpr(TypeReference type, int s, int e) { boolean array = type instanceof ArrayTypeReference; if (array) return new NullLiteral(s, e); diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index fa887974..bb495fbc 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -155,11 +155,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (bfd.singularData != null && isDefault != null) { isDefault.addError("@Builder.Default and @Singular cannot be mixed."); + findAnnotation(Builder.Default.class, fieldNode, true); isDefault = null; } if (fd.init == null && isDefault != null) { isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); + findAnnotation(Builder.Default.class, fieldNode, true); isDefault = null; } diff --git a/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults.java b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults.java new file mode 100644 index 00000000..81d59a37 --- /dev/null +++ b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults.java @@ -0,0 +1,61 @@ +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Value; +import lombok.Builder; +final @NoArgsConstructor @AllArgsConstructor @Builder @Value class ConstructorsWithBuilderDefaults { + public static @java.lang.SuppressWarnings("all") class ConstructorsWithBuilderDefaultsBuilder { + private @java.lang.SuppressWarnings("all") int x; + private @java.lang.SuppressWarnings("all") boolean x$set; + @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder() { + super(); + } + public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder x(final int x) { + this.x = x; + x$set = true; + return this; + } + public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults build() { + return new ConstructorsWithBuilderDefaults((x$set ? x : ConstructorsWithBuilderDefaults.$default$x())); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ConstructorsWithBuilderDefaults.ConstructorsWithBuilderDefaultsBuilder(x=" + this.x) + ")"); + } + } + private final @Builder.Default int x; + private static @java.lang.SuppressWarnings("all") int $default$x() { + return 5; + } + public static @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder builder() { + return new ConstructorsWithBuilderDefaultsBuilder(); + } + public @java.lang.SuppressWarnings("all") int getX() { + return this.x; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) { + if ((o == this)) + return true; + if ((! (o instanceof ConstructorsWithBuilderDefaults))) + return false; + final ConstructorsWithBuilderDefaults other = (ConstructorsWithBuilderDefaults) o; + if ((this.getX() != other.getX())) + return false; + return true; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() { + final int PRIME = 59; + int result = 1; + result = ((result * PRIME) + this.getX()); + return result; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ConstructorsWithBuilderDefaults(x=" + this.getX()) + ")"); + } + public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults() { + super(); + this.x = ConstructorsWithBuilderDefaults.$default$x(); + } + public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults(final int x) { + super(); + this.x = x; + } +} diff --git a/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults2.java b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults2.java new file mode 100644 index 00000000..6094e2f4 --- /dev/null +++ b/test/transform/resource/after-ecj/ConstructorsWithBuilderDefaults2.java @@ -0,0 +1,60 @@ +import lombok.NoArgsConstructor; +import lombok.Value; +import lombok.Builder; +final @Builder @Value class ConstructorsWithBuilderDefaults { + public static @java.lang.SuppressWarnings("all") class ConstructorsWithBuilderDefaultsBuilder { + private @java.lang.SuppressWarnings("all") int x; + private @java.lang.SuppressWarnings("all") boolean x$set; + @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder() { + super(); + } + public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder x(final int x) { + this.x = x; + x$set = true; + return this; + } + public @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults build() { + return new ConstructorsWithBuilderDefaults((x$set ? x : ConstructorsWithBuilderDefaults.$default$x())); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ConstructorsWithBuilderDefaults.ConstructorsWithBuilderDefaultsBuilder(x=" + this.x) + ")"); + } + } + private final @Builder.Default int x; + private static @java.lang.SuppressWarnings("all") int $default$x() { + return 5; + } + @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults(final int x) { + super(); + this.x = x; + } + public static @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaultsBuilder builder() { + return new ConstructorsWithBuilderDefaultsBuilder(); + } + public @java.lang.SuppressWarnings("all") int getX() { + return this.x; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) { + if ((o == this)) + return true; + if ((! (o instanceof ConstructorsWithBuilderDefaults))) + return false; + final ConstructorsWithBuilderDefaults other = (ConstructorsWithBuilderDefaults) o; + if ((this.getX() != other.getX())) + return false; + return true; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() { + final int PRIME = 59; + int result = 1; + result = ((result * PRIME) + this.getX()); + return result; + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ConstructorsWithBuilderDefaults(x=" + this.getX()) + ")"); + } + private @java.lang.SuppressWarnings("all") ConstructorsWithBuilderDefaults() { + super(); + this.x = ConstructorsWithBuilderDefaults.$default$x(); + } +}
\ No newline at end of file |