[fixes #1812] @Singular marked collections which nevertheless somehow ended up null would cause an NPE during toBuilder() invocations.

This commit is contained in:
Reinier Zwitserloot committed 2018-08-21 00:02:12 +02:00
1 parent 35fe755b87
commit ca35539f7c
9 files changed
+206 -42

No files matched your search

@@ -24,6 +24,7 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.EclipseAugments.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.setGeneratedBy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -1862,4 +1863,29 @@ public class EclipseHandlerUtil {
String p = typeDecl.superclass.toString();
return p.equals("Object") || p.equals("java.lang.Object");
}
public static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
NameReference ref;
if (varNames.length > 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE);
else ref = new SingleNameReference(varNames[0], p);
setGeneratedBy(ref, source);
return ref;
}
public static TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
TypeReference ref;
long[] poss = Eclipse.poss(source, varNames.length);
if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss);
else ref = new SingleTypeReference(varNames[0], p);
setGeneratedBy(ref, source);
return ref;
}
}
@@ -40,6 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -47,6 +48,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
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.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
@@ -492,6 +494,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
}
private static final char[] EMPTY_LIST = "emptyList".toCharArray();
private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source) {
// return new ThingieBuilder<A, B>().setA(this.a).setB(this.b);
@@ -511,19 +514,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
for (BuilderFieldData bfd : builderFields) {
char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray();
MessageSend ms = new MessageSend();
Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2];
if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
FieldReference fr = new FieldReference(fieldName, 0);
fr.receiver = new ThisReference(0, 0);
ms.arguments = new Expression[] {fr};
for (int i = 0; i < tgt.length; i++) {
FieldReference fr = new FieldReference(fieldName, 0);
fr.receiver = new ThisReference(0, 0);
tgt[i] = fr;
}
} else {
String obtainName = bfd.obtainVia.method();
boolean obtainIsStatic = bfd.obtainVia.isStatic();
MessageSend obtainExpr = new MessageSend();
obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
obtainExpr.selector = obtainName.toCharArray();
if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
ms.arguments = new Expression[] {obtainExpr};
for (int i = 0; i < tgt.length; i++) {
MessageSend obtainExpr = new MessageSend();
obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
obtainExpr.selector = obtainName.toCharArray();
if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
tgt[i] = obtainExpr;
}
}
if (bfd.singularData == null) {
ms.arguments = tgt;
} else {
Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
MessageSend emptyList = new MessageSend();
emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray());
emptyList.selector = EMPTY_LIST;
ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])};
}
ms.receiver = receiver;
ms.selector = setterName;
@@ -69,7 +69,6 @@ import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
@@ -819,29 +818,4 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
expr.sourceStart = pS; expr.sourceEnd = pE;
return expr;
}
public NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
NameReference ref;
if (varNames.length > 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE);
else ref = new SingleNameReference(varNames[0], p);
setGeneratedBy(ref, source);
return ref;
}
public TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
TypeReference ref;
long[] poss = Eclipse.poss(source, varNames.length);
if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss);
else ref = new SingleTypeReference(varNames[0], p);
setGeneratedBy(ref, source);
return ref;
}
}
@@ -492,18 +492,34 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCExpression invoke = call;
for (BuilderFieldData bfd : builderFields) {
Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString()));
JCExpression arg;
JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2];
if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
arg = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
for (int i = 0; i < tgt.length; i++) {
tgt[i] = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
}
} else {
if (bfd.obtainVia.isStatic()) {
JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method()));
arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName("this"))));
for (int i = 0; i < tgt.length; i++) {
JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method()));
tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName("this"))));
}
} else {
JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method()));
arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil());
for (int i = 0; i < tgt.length; i++) {
JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method()));
tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil());
}
}
}
JCExpression arg;
if (bfd.singularData == null) {
arg = tgt[0];
} else {
JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null));
JCExpression emptyList = maker.Apply(List.<JCExpression>nil(), chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil());
arg = maker.Conditional(eqNull, emptyList, tgt[1]);
}
invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg));
}
JCStatement statement = maker.Return(invoke);
@@ -0,0 +1,63 @@
class BuilderSingularToBuilderWithNull {
private java.util.List<String> elems;
public static void test() {
new BuilderSingularToBuilderWithNull(null).toBuilder();
}
@java.lang.SuppressWarnings("all")
BuilderSingularToBuilderWithNull(final java.util.List<String> elems) {
this.elems = elems;
}
@java.lang.SuppressWarnings("all")
public static class BuilderSingularToBuilderWithNullBuilder {
@java.lang.SuppressWarnings("all")
private java.util.ArrayList<String> elems;
@java.lang.SuppressWarnings("all")
BuilderSingularToBuilderWithNullBuilder() {
}
@java.lang.SuppressWarnings("all")
public BuilderSingularToBuilderWithNullBuilder elem(final String elem) {
if (this.elems == null) this.elems = new java.util.ArrayList<String>();
this.elems.add(elem);
return this;
}
@java.lang.SuppressWarnings("all")
public BuilderSingularToBuilderWithNullBuilder elems(final java.util.Collection<? extends String> elems) {
if (this.elems == null) this.elems = new java.util.ArrayList<String>();
this.elems.addAll(elems);
return this;
}
@java.lang.SuppressWarnings("all")
public BuilderSingularToBuilderWithNullBuilder clearElems() {
if (this.elems != null) this.elems.clear();
return this;
}
@java.lang.SuppressWarnings("all")
public BuilderSingularToBuilderWithNull build() {
java.util.List<String> elems;
switch (this.elems == null ? 0 : this.elems.size()) {
case 0:
elems = java.util.Collections.emptyList();
break;
case 1:
elems = java.util.Collections.singletonList(this.elems.get(0));
break;
default:
elems = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(this.elems));
}
return new BuilderSingularToBuilderWithNull(elems);
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public java.lang.String toString() {
return "BuilderSingularToBuilderWithNull.BuilderSingularToBuilderWithNullBuilder(elems=" + this.elems + ")";
}
}
@java.lang.SuppressWarnings("all")
public static BuilderSingularToBuilderWithNullBuilder builder() {
return new BuilderSingularToBuilderWithNullBuilder();
}
@java.lang.SuppressWarnings("all")
public BuilderSingularToBuilderWithNullBuilder toBuilder() {
return new BuilderSingularToBuilderWithNullBuilder().elems(this.elems == null ? java.util.Collections.emptyList() : this.elems);
}
}
@@ -86,7 +86,7 @@ class BuilderWithToBuilder<T> {
}
@java.lang.SuppressWarnings("all")
public BuilderWithToBuilderBuilder<T> toBuilder() {
return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars);
return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars == null ? java.util.Collections.emptyList() : this.bars);
}
}
class ConstructorWithToBuilder<T> {
@@ -0,0 +1,57 @@
import lombok.Singular;
@lombok.Builder(toBuilder = true) class BuilderSingularToBuilderWithNull {
public static @java.lang.SuppressWarnings("all") class BuilderSingularToBuilderWithNullBuilder {
private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> elems;
@java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder() {
super();
}
public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder elem(String elem) {
if ((this.elems == null))
this.elems = new java.util.ArrayList<String>();
this.elems.add(elem);
return this;
}
public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder elems(java.util.Collection<? extends String> elems) {
if ((this.elems == null))
this.elems = new java.util.ArrayList<String>();
this.elems.addAll(elems);
return this;
}
public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder clearElems() {
if ((this.elems != null))
this.elems.clear();
return this;
}
public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNull build() {
java.util.List<String> elems;
switch (((this.elems == null) ? 0 : this.elems.size())) {
case 0 :
elems = java.util.Collections.emptyList();
break;
case 1 :
elems = java.util.Collections.singletonList(this.elems.get(0));
break;
default :
elems = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(this.elems));
}
return new BuilderSingularToBuilderWithNull(elems);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
return (("BuilderSingularToBuilderWithNull.BuilderSingularToBuilderWithNullBuilder(elems=" + this.elems) + ")");
}
}
private @Singular java.util.List<String> elems;
public static void test() {
new BuilderSingularToBuilderWithNull(null).toBuilder();
}
@java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNull(final java.util.List<String> elems) {
super();
this.elems = elems;
}
public static @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder builder() {
return new BuilderSingularToBuilderWithNullBuilder();
}
public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder toBuilder() {
return new BuilderSingularToBuilderWithNullBuilder().elems(((this.elems == null) ? java.util.Collections.emptyList() : this.elems));
}
}
@@ -74,7 +74,7 @@ import lombok.Builder;
return new BuilderWithToBuilderBuilder<T>();
}
public @java.lang.SuppressWarnings("all") BuilderWithToBuilderBuilder<T> toBuilder() {
return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars);
return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(((this.bars == null) ? java.util.Collections.emptyList() : this.bars));
}
}
@lombok.experimental.Accessors(prefix = "m") class ConstructorWithToBuilder<T> {
@@ -0,0 +1,10 @@
import lombok.Singular;
@lombok.Builder(toBuilder = true)
class BuilderSingularToBuilderWithNull {
@Singular private java.util.List<String> elems;
public static void test() {
new BuilderSingularToBuilderWithNull(null).toBuilder();
}
}