aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/javac
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lombok/javac')
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java272
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java91
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java94
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldDefaults.java81
-rw-r--r--src/core/lombok/javac/handlers/HandleHelper.java138
-rw-r--r--src/core/lombok/javac/handlers/HandleNonNull.java3
-rw-r--r--src/core/lombok/javac/handlers/HandleUtilityClass.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java14
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java35
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java6
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java2
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java10
12 files changed, 579 insertions, 169 deletions
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index ab833195..efe40da3 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -31,6 +31,7 @@ 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.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
@@ -51,6 +52,7 @@ import com.sun.tools.javac.util.Name;
import lombok.AccessLevel;
import lombok.Builder;
+import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
import lombok.Singular;
import lombok.core.AST.Kind;
@@ -81,8 +83,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
private static class BuilderFieldData {
JCExpression type;
+ Name rawName;
Name name;
SingularData singularData;
+ ObtainVia obtainVia;
+ JavacNode obtainViaNode;
java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
}
@@ -97,6 +102,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
+ String toBuilderMethodName = "toBuilder";
+ boolean toBuilder = builderInstance.toBuilder();
+ java.util.List<Name> typeArgsForToBuilder = null;
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) buildMethodName = "build";
@@ -138,14 +146,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
// Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
BuilderFieldData bfd = new BuilderFieldData();
+ bfd.rawName = fd.name;
bfd.name = removePrefixFromField(fieldNode);
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode);
+ addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
allFields.append(fieldNode);
}
- new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
@@ -171,14 +181,15 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCClassDecl td = (JCClassDecl) tdParent.get();
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
isStatic = (jmd.mods.flags & Flags.STATIC) != 0;
- returnType = jmd.restype;
+ JCExpression fullReturnType = jmd.restype;
+ returnType = fullReturnType;
typeParams = jmd.typarams;
thrownExceptions = jmd.thrown;
nameOfBuilderMethod = jmd.name;
+ if (returnType instanceof JCTypeApply) {
+ returnType = ((JCTypeApply) returnType).clazz;
+ }
if (builderClassName.isEmpty()) {
- if (returnType instanceof JCTypeApply) {
- returnType = ((JCTypeApply) returnType).clazz;
- }
if (returnType instanceof JCFieldAccess) {
builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
} else if (returnType instanceof JCIdent) {
@@ -196,13 +207,76 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (Character.isLowerCase(builderClassName.charAt(0))) {
builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
}
-
} else {
// This shouldn't happen.
System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
builderClassName = td.name.toString() + "Builder";
}
}
+ if (toBuilder) {
+ final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
+ if (returnType instanceof JCArrayTypeTree) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ Name simpleName;
+ String pkg;
+ List<JCExpression> tpOnRet = List.nil();
+
+ if (fullReturnType instanceof JCTypeApply) {
+ tpOnRet = ((JCTypeApply) fullReturnType).arguments;
+ }
+
+ if (returnType instanceof JCIdent) {
+ simpleName = ((JCIdent) returnType).name;
+ pkg = null;
+ } else if (returnType instanceof JCFieldAccess) {
+ JCFieldAccess jcfa = (JCFieldAccess) returnType;
+ simpleName = jcfa.name;
+ pkg = unpack(jcfa.selected);
+ if (pkg.startsWith("ERR:")) {
+ String err = pkg.substring(4, pkg.indexOf("__ERR__"));
+ annotationNode.addError(err);
+ return;
+ }
+ } else {
+ annotationNode.addError("Expected a (parameterized) type here instead of a " + returnType.getClass().getName());
+ return;
+ }
+
+ if (pkg != null && !parent.getPackageDeclaration().equals(pkg)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (!tdParent.getName().contentEquals(simpleName)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ List<JCTypeParameter> tpOnMethod = jmd.typarams;
+ List<JCTypeParameter> tpOnType = ((JCClassDecl) tdParent.get()).typarams;
+ typeArgsForToBuilder = new ArrayList<Name>();
+
+ for (JCTypeParameter tp : tpOnMethod) {
+ int pos = -1;
+ int idx = -1;
+ for (JCExpression tOnRet : tpOnRet) {
+ idx++;
+ if (!(tOnRet instanceof JCIdent)) continue;
+ if (((JCIdent) tOnRet).name != tp.name) continue;
+ pos = idx;
+ }
+
+ if (pos == -1 || tpOnType.size() <= pos) {
+ annotationNode.addError("**" + returnType.getClass().toString());
+// annotationNode.addError("@Builder(toBuilder=true) requires that each type parameter on the static method is part of the typeargs of the return value. Type parameter " + tp.name + " is not part of the return type.");
+ return;
+ }
+ typeArgsForToBuilder.add(tpOnType.get(pos).name);
+ }
+ }
} else {
annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
return;
@@ -214,8 +288,10 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
BuilderFieldData bfd = new BuilderFieldData();
JCVariableDecl raw = (JCVariableDecl) param.get();
bfd.name = raw.name;
+ bfd.rawName = raw.name;
bfd.type = raw.vartype;
bfd.singularData = getSingularData(param);
+ addObtainVia(bfd, param);
builderFields.add(bfd);
}
}
@@ -254,6 +330,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
break;
}
}
+ if (bfd.obtainVia != null) {
+ if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
+ bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
+ return;
+ }
+ if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
+ bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
+ return;
+ }
+ }
}
generateBuilderFields(builderType, builderFields, ast);
@@ -264,7 +350,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), null, annotationNode);
+ JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, null, annotationNode);
if (cd != null) injectMethod(builderType, cd);
}
@@ -294,9 +380,95 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (md != null) injectMethod(tdParent, md);
}
+ if (toBuilder) {
+ switch (methodExists(toBuilderMethodName, tdParent, 0)) {
+ case EXISTS_BY_USER:
+ annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+ return;
+ case NOT_EXISTS:
+ List<JCTypeParameter> tps = typeParams;
+ if (typeArgsForToBuilder != null) {
+ ListBuffer<JCTypeParameter> lb = new ListBuffer<JCTypeParameter>();
+ JavacTreeMaker maker = tdParent.getTreeMaker();
+ for (Name n : typeArgsForToBuilder) {
+ lb.append(maker.TypeParameter(n, List.<JCExpression>nil()));
+ }
+ tps = lb.toList();
+ }
+ JCMethodDecl md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
}
+ private static String unpack(JCExpression expr) {
+ StringBuilder sb = new StringBuilder();
+ unpack(sb, expr);
+ return sb.toString();
+ }
+
+ private static void unpack(StringBuilder sb, JCExpression expr) {
+ if (expr instanceof JCIdent) {
+ sb.append(((JCIdent) expr).name.toString());
+ return;
+ }
+
+ if (expr instanceof JCFieldAccess) {
+ JCFieldAccess jcfa = (JCFieldAccess) expr;
+ unpack(sb, jcfa.selected);
+ sb.append(".").append(jcfa.name.toString());
+ return;
+ }
+
+ if (expr instanceof JCTypeApply) {
+ sb.setLength(0);
+ sb.append("ERR:");
+ sb.append("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate.");
+ sb.append("__ERR__");
+ return;
+ }
+
+ sb.setLength(0);
+ sb.append("ERR:");
+ sb.append("Expected a type of some sort, not a " + expr.getClass().getName());
+ sb.append("__ERR__");
+ }
+
+ private JCMethodDecl generateToBuilderMethod(String toBuilderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, boolean fluent, JCAnnotation ast) {
+ // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b);
+ JavacTreeMaker maker = type.getTreeMaker();
+
+ ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
+ for (JCTypeParameter typeParam : typeParams) {
+ typeArgs.append(maker.Ident(typeParam.name));
+ }
+
+ JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null);
+ JCExpression invoke = call;
+ for (BuilderFieldData bfd : builderFields) {
+ Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString()));
+ JCExpression arg;
+ 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()));
+ } 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"))));
+ } 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());
+ }
+ }
+ invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg));
+ }
+ JCStatement statement = maker.Return(invoke);
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
JavacTreeMaker maker = type.getTreeMaker();
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
@@ -465,6 +637,17 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return injectType(tdParent, builder);
}
+ private void addObtainVia(BuilderFieldData bfd, JavacNode node) {
+ for (JavacNode child : node.down()) {
+ if (!annotationTypeMatches(ObtainVia.class, child)) continue;
+ AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child);
+ bfd.obtainVia = ann.getInstance();
+ bfd.obtainViaNode = child;
+ deleteAnnotationIfNeccessary(child, ObtainVia.class);
+ return;
+ }
+ }
+
/**
* Returns the explicitly requested singular annotation on this node (field
* or parameter), or null if there's no {@code @Singular} annotation on it.
@@ -473,48 +656,47 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
*/
private SingularData getSingularData(JavacNode node) {
for (JavacNode child : node.down()) {
- if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
- Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
- AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
- deleteAnnotationIfNeccessary(child, Singular.class);
- String explicitSingular = ann.getInstance().value();
- if (explicitSingular.isEmpty()) {
- if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
- node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ if (!annotationTypeMatches(Singular.class, child)) continue;
+ Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
+ AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
+ deleteAnnotationIfNeccessary(child, Singular.class);
+ String explicitSingular = ann.getInstance().value();
+ if (explicitSingular.isEmpty()) {
+ if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
+ node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ explicitSingular = pluralName.toString();
+ } else {
+ explicitSingular = autoSingularize(node.getName());
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
explicitSingular = pluralName.toString();
- } else {
- explicitSingular = autoSingularize(node.getName());
- if (explicitSingular == null) {
- node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
- explicitSingular = pluralName.toString();
- }
}
}
- Name singularName = node.toName(explicitSingular);
-
- JCExpression type = null;
- if (node.get() instanceof JCVariableDecl) {
- type = ((JCVariableDecl) node.get()).vartype;
- }
-
- String name = null;
- List<JCExpression> typeArgs = List.nil();
- if (type instanceof JCTypeApply) {
- typeArgs = ((JCTypeApply) type).arguments;
- type = ((JCTypeApply) type).clazz;
- }
-
- name = type.toString();
-
- String targetFqn = JavacSingularsRecipes.get().toQualified(name);
- JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
- if (singularizer == null) {
- node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
- return null;
- }
-
- return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
}
+ Name singularName = node.toName(explicitSingular);
+
+ JCExpression type = null;
+ if (node.get() instanceof JCVariableDecl) {
+ type = ((JCVariableDecl) node.get()).vartype;
+ }
+
+ String name = null;
+ List<JCExpression> typeArgs = List.nil();
+ if (type instanceof JCTypeApply) {
+ typeArgs = ((JCTypeApply) type).arguments;
+ type = ((JCTypeApply) type).clazz;
+ }
+
+ name = type.toString();
+
+ String targetFqn = JavacSingularsRecipes.get().toQualified(name);
+ JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
}
return null;
diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java
index c5b309c2..4a4ec49c 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.java
@@ -23,6 +23,8 @@ package lombok.javac.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
+import static lombok.javac.Javac.*;
+
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -41,13 +43,13 @@ 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.JCAnnotation;
-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.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
@@ -71,8 +73,9 @@ public class HandleConstructor {
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- List<JavacNode> fields = List.nil();
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, SkipIfConstructorExists.NO, null, annotationNode);
+ boolean force = ann.force();
+ List<JavacNode> fields = force ? findFinalFields(typeNode) : List.<JavacNode>nil();
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, null, annotationNode);
}
}
@@ -97,11 +100,19 @@ public class HandleConstructor {
suppressConstructorProperties = suppress;
}
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
public static List<JavacNode> findRequiredFields(JavacNode typeNode) {
+ return findFields(typeNode, true);
+ }
+
+ public static List<JavacNode> findFinalFields(JavacNode typeNode) {
+ return findFields(typeNode, false);
+ }
+
+ public static List<JavacNode> findFields(JavacNode typeNode, boolean nullMarked) {
ListBuffer<JavacNode> fields = new ListBuffer<JavacNode>();
for (JavacNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -112,7 +123,7 @@ public class HandleConstructor {
//Skip static fields.
if ((fieldFlags & Flags.STATIC) != 0) continue;
boolean isFinal = (fieldFlags & Flags.FINAL) != 0;
- boolean isNonNull = !findAnnotations(child, NON_NULL_PATTERN).isEmpty();
+ boolean isNonNull = nullMarked && !findAnnotations(child, NON_NULL_PATTERN).isEmpty();
if ((isFinal || isNonNull) && fieldDecl.init == null) fields.append(child);
}
return fields.toList();
@@ -138,7 +149,7 @@ public class HandleConstructor {
boolean suppress = ann.suppressConstructorProperties();
suppressConstructorProperties = suppress;
}
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
@@ -174,7 +185,7 @@ public class HandleConstructor {
}
public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), staticName, skipIfConstructorExists, null, source);
+ generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, source);
}
public enum SkipIfConstructorExists {
@@ -182,10 +193,10 @@ public class HandleConstructor {
}
public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), staticName, skipIfConstructorExists, null, source);
+ generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, source);
}
- public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source) {
+ public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
@@ -193,8 +204,8 @@ public class HandleConstructor {
for (JavacNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
boolean skipGeneration = annotationTypeMatches(NoArgsConstructor.class, child) ||
- annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child);
+ annotationTypeMatches(AllArgsConstructor.class, child) ||
+ annotationTypeMatches(RequiredArgsConstructor.class, child);
if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
skipGeneration = annotationTypeMatches(Builder.class, child);
@@ -214,10 +225,10 @@ public class HandleConstructor {
}
}
- JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, suppressConstructorProperties, source);
+ JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, suppressConstructorProperties, source);
injectMethod(typeNode, constr);
if (staticConstrRequired) {
- JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, fields, source.get());
+ JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, allToDefault ? List.<JavacNode>nil() : fields, source.get());
injectMethod(typeNode, staticConstr);
}
}
@@ -236,7 +247,7 @@ public class HandleConstructor {
mods.annotations = mods.annotations.append(annotation);
}
- public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, Boolean suppressConstructorProperties, JavacNode source) {
+ public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean allToDefault, Boolean suppressConstructorProperties, JavacNode source) {
JavacTreeMaker maker = typeNode.getTreeMaker();
boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
@@ -259,29 +270,55 @@ public class HandleConstructor {
Name fieldName = removePrefixFromField(fieldNode);
Name rawName = field.name;
List<JCAnnotation> nonNulls = findAnnotations(fieldNode, NON_NULL_PATTERN);
- List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN);
- long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
- JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null);
- params.append(param);
+ if (!allToDefault) {
+ List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN);
+ long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null);
+ params.append(param);
+ if (!nonNulls.isEmpty()) {
+ JCStatement nullCheck = generateNullCheck(maker, fieldNode, source);
+ if (nullCheck != null) nullChecks.append(nullCheck);
+ }
+ }
JCFieldAccess thisX = maker.Select(maker.Ident(fieldNode.toName("this")), rawName);
- JCAssign assign = maker.Assign(thisX, maker.Ident(fieldName));
+ JCExpression assign = maker.Assign(thisX, allToDefault ? getDefaultExpr(maker, field.vartype) : maker.Ident(fieldName));
assigns.append(maker.Exec(assign));
-
- if (!nonNulls.isEmpty()) {
- JCStatement nullCheck = generateNullCheck(maker, fieldNode, source);
- if (nullCheck != null) nullChecks.append(nullCheck);
- }
}
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
- if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
+ if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
addConstructorProperties(mods, typeNode, fields);
}
if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor));
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
- null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
- maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext());
+ null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
+ maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext());
+ }
+
+ private static JCExpression getDefaultExpr(JavacTreeMaker maker, JCExpression type) {
+ if (type instanceof JCPrimitiveTypeTree) {
+ switch (((JCPrimitiveTypeTree) type).getPrimitiveTypeKind()) {
+ case BOOLEAN:
+ return maker.Literal(CTC_BOOLEAN, 0);
+ case CHAR:
+ return maker.Literal(CTC_CHAR, 0);
+ default:
+ case BYTE:
+ case SHORT:
+ case INT:
+ return maker.Literal(CTC_INT, 0);
+ case LONG:
+ return maker.Literal(CTC_LONG, 0L);
+ case FLOAT:
+ return maker.Literal(CTC_FLOAT, 0F);
+ case DOUBLE:
+ return maker.Literal(CTC_DOUBLE, 0D);
+ }
+ }
+
+ return maker.Literal(CTC_BOT, null);
+
}
public static boolean isLocalType(JavacNode type) {
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 1a6f4a8f..4bc79f03 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -124,10 +124,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
public void generateMethods(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
+
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
- long flags = ((JCClassDecl)typeNode.get()).mods.flags;
+ long flags = ((JCClassDecl) typeNode.get()).mods.flags;
notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
}
@@ -140,7 +141,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
boolean implicitCallSuper = callSuper == null;
if (callSuper == null) {
try {
- callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ callSuper = ((Boolean) EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {
throw new InternalError("Lombok bug - this cannot happen - can't find callSuper field in EqualsAndHashCode annotation.");
}
@@ -184,7 +185,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
}
- boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
+ boolean isFinal = (((JCClassDecl) typeNode.get()).mods.flags & Flags.FINAL) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
@@ -202,8 +203,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
// The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 2 methods are
// all inter-related and should be written by the same entity.
String msg = String.format("Not generating %s: One of equals or hashCode exists. " +
- "You should either write both of these or none of these (in the latter case, lombok generates them).",
- equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
+ "You should either write both of these or none of these (in the latter case, lombok generates them).",
+ equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
source.addWarning(msg);
}
return;
@@ -248,8 +249,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")),
- List.<JCExpression>nil());
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")),
+ List.<JCExpression>nil());
statements.append(createResultCalculation(typeNode, callToSuper));
}
@@ -258,14 +259,14 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression fType = getFieldType(fieldNode, fieldAccess);
JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
if (fType instanceof JCPrimitiveTypeTree) {
- switch (((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind()) {
+ switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
case BOOLEAN:
/* this.fieldName ? X : Y */
- statements.append(createResultCalculation(typeNode, maker.Conditional(fieldAccessor,
- maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse()))));
+ statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(fieldAccessor,
+ maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse())))));
break;
case LONG: {
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), fieldAccessor));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
@@ -273,17 +274,17 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
case FLOAT:
/* Float.floatToIntBits(this.fieldName) */
statements.append(createResultCalculation(typeNode, maker.Apply(
- List.<JCExpression>nil(),
- genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"),
- List.of(fieldAccessor))));
+ List.<JCExpression>nil(),
+ genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"),
+ List.of(fieldAccessor))));
break;
case DOUBLE: {
/* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
JCExpression init = maker.Apply(
- List.<JCExpression>nil(),
- genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
- List.of(fieldAccessor));
+ List.<JCExpression>nil(),
+ genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
+ List.of(fieldAccessor));
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), init));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
@@ -299,23 +300,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
} else if (fType instanceof JCArrayTypeTree) {
/* java.util.Arrays.deepHashCode(this.fieldName) //use just hashCode() for primitive arrays. */
- boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree;
- boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
+ boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepHC = multiDim || !primitiveArray;
JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepHC ? "deepHashCode" : "hashCode");
statements.append(createResultCalculation(typeNode, maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor))));
} else /* objects */ {
/* final java.lang.Object $fieldName = this.fieldName; */
- /* $fieldName == null ? 0 : $fieldName.hashCode() */
+ /* ($fieldName == null ? NULL_PRIME : $fieldName.hashCode()) */
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, genJavaLangTypeRef(typeNode, "Object"), fieldAccessor));
JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")),
- List.<JCExpression>nil());
+ List.<JCExpression>nil());
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(dollarFieldName), maker.Literal(CTC_BOT, null));
- statements.append(createResultCalculation(typeNode, maker.Conditional(thisEqualsNull, maker.Literal(0), hcCall)));
+ statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(thisEqualsNull, maker.Literal(HandlerUtil.primeForNull()), hcCall))));
}
}
@@ -325,11 +326,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCBlock body = maker.Block(0, statements.toList());
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("hashCode"), returnType,
- List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
+ List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
}
public JCExpressionStatement createResultCalculation(JavacNode typeNode, JCExpression expr) {
- /* result = result * PRIME + (expr); */
+ /* result = result * PRIME + expr; */
JavacTreeMaker maker = typeNode.getTreeMaker();
Name resultName = typeNode.toName(RESULT_NAME);
JCExpression mult = maker.Binary(CTC_MUL, maker.Ident(resultName), maker.Ident(typeNode.toName(PRIME_NAME)));
@@ -339,10 +340,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/** The 2 references must be clones of each other. */
public JCExpression longToIntForHashCode(JavacTreeMaker maker, JCExpression ref1, JCExpression ref2) {
- /* (int)(ref >>> 32 ^ ref) */
+ /* (int) (ref >>> 32 ^ ref) */
JCExpression shift = maker.Binary(CTC_UNSIGNED_SHIFT_RIGHT, ref1, maker.Literal(32));
JCExpression xorBits = maker.Binary(CTC_BITXOR, shift, ref2);
- return maker.TypeCast(maker.TypeIdent(CTC_INT), xorBits);
+ return maker.TypeCast(maker.TypeIdent(CTC_INT), maker.Parens(xorBits));
}
public JCExpression createTypeReference(JavacNode type) {
@@ -385,12 +386,12 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* if (o == this) return true; */ {
statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(oName),
- maker.Ident(thisName)), returnBool(maker, true), null));
+ maker.Ident(thisName)), returnBool(maker, true), null));
}
- /* if (!(o instanceof Outer.Inner.MyType) return false; */ {
+ /* if (!(o instanceof Outer.Inner.MyType)) return false; */ {
- JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode)));
+ JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.Parens(maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode))));
statements.append(maker.If(notInstanceOf, returnBool(maker, false), null));
}
@@ -413,7 +414,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
statements.append(
- maker.VarDef(maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
+ maker.VarDef(maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
}
}
@@ -423,8 +424,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression thisRef = maker.Ident(thisName);
JCExpression castThisRef = maker.TypeCast(genJavaLangTypeRef(typeNode, "Object"), thisRef);
JCExpression equalityCheck = maker.Apply(exprNil,
- maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
- List.of(castThisRef));
+ maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
+ List.of(castThisRef));
statements.append(maker.If(maker.Unary(CTC_NOT, equalityCheck), returnBool(maker, false), null));
}
}
@@ -432,8 +433,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* if (!super.equals(o)) return false; */
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
- List.<JCExpression>of(maker.Ident(oName)));
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
+ List.<JCExpression>of(maker.Ident(oName)));
JCUnary superNotEqual = maker.Unary(CTC_NOT, callToSuper);
statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
}
@@ -462,19 +463,19 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
} else if (fType instanceof JCArrayTypeTree) {
/* if (!java.util.Arrays.deepEquals(this.fieldName, other.fieldName)) return false; //use equals for primitive arrays. */
- boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree;
- boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
+ boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepEquals = multiDim || !primitiveArray;
JCExpression eqMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals");
List<JCExpression> args = List.of(thisFieldAccessor, otherFieldAccessor);
statements.append(maker.If(maker.Unary(CTC_NOT,
- maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null));
+ maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null));
} else /* objects */ {
/* final java.lang.Object this$fieldName = this.fieldName; */
/* final java.lang.Object other$fieldName = other.fieldName; */
- /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */
- Name fieldName = ((JCVariableDecl)fieldNode.get()).name;
+ /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
+ Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
Name thisDollarFieldName = thisDollar.append(fieldName);
Name otherDollarFieldName = otherDollar.append(fieldName);
@@ -484,8 +485,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(thisDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression otherNotEqualsNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(otherDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression thisEqualsThat = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")),
- List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
+ maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")),
+ List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
JCExpression fieldsAreNotEqual = maker.Conditional(thisEqualsNull, otherNotEqualsNull, maker.Unary(CTC_NOT, thisEqualsThat));
statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null));
}
@@ -521,12 +522,13 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
public JCStatement generateCompareFloatOrDouble(JCExpression thisDotField, JCExpression otherDotField,
- JavacTreeMaker maker, JavacNode node, boolean isDouble) {
+ JavacTreeMaker maker, JavacNode node, boolean isDouble) {
+
/* if (Float.compare(fieldName, other.fieldName) != 0) return false; */
JCExpression clazz = genJavaLangTypeRef(node, isDouble ? "Double" : "Float");
List<JCExpression> args = List.of(thisDotField, otherDotField);
JCBinary compareCallEquals0 = maker.Binary(CTC_NOT_EQUAL, maker.Apply(
- List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0));
+ List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0));
return maker.If(compareCallEquals0, returnBool(maker, false), null);
}
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index 335ab1fe..12c22059 100644
--- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Project Lombok Authors.
+ * Copyright (C) 2012-2015 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
@@ -31,23 +31,24 @@ import lombok.core.HandlerPriority;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
import lombok.experimental.PackagePrivate;
-import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacASTAdapter;
+import lombok.javac.JavacASTVisitor;
import lombok.javac.JavacNode;
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.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
-import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
/**
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
-@ProviderFor(JavacAnnotationHandler.class)
+@ProviderFor(JavacASTVisitor.class)
@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
-public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
+public class HandleFieldDefaults extends JavacASTAdapter {
public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
if (hasAnnotation(FieldDefaults.class, typeNode)) {
@@ -72,13 +73,13 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
//Skip fields that start with $
if (fieldDecl.name.toString().startsWith("$")) continue;
- setFieldDefaultsForField(field, errorNode.get(), level, makeFinal);
+ setFieldDefaultsForField(field, level, makeFinal);
}
return true;
}
- public void setFieldDefaultsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean makeFinal) {
+ public void setFieldDefaultsForField(JavacNode fieldNode, AccessLevel level, boolean makeFinal) {
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
if (level != null && level != AccessLevel.NONE) {
if ((field.mods.flags & (Flags.PUBLIC | Flags.PRIVATE | Flags.PROTECTED)) == 0) {
@@ -90,38 +91,62 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
if (makeFinal && (field.mods.flags & Flags.FINAL) == 0) {
if (!hasAnnotationAndDeleteIfNeccessary(NonFinal.class, fieldNode)) {
- field.mods.flags |= Flags.FINAL;
+ if ((field.mods.flags & Flags.STATIC) == 0 || field.init != null) {
+ field.mods.flags |= Flags.FINAL;
+ }
}
}
fieldNode.rebuild();
}
- @Override public void handle(AnnotationValues<FieldDefaults> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
-
- deleteAnnotationIfNeccessary(annotationNode, FieldDefaults.class);
- deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
- JavacNode node = annotationNode.up();
- FieldDefaults instance = annotation.getInstance();
- AccessLevel level = instance.level();
- boolean makeFinal = instance.makeFinal();
+ @Override public void visitType(JavacNode typeNode, JCClassDecl type) {
+ AnnotationValues<FieldDefaults> fieldDefaults = null;
+ JavacNode source = typeNode;
- if (level == AccessLevel.NONE && !makeFinal) {
- annotationNode.addError("This does nothing; provide either level or makeFinal or both.");
- return;
+ boolean levelIsExplicit = false;
+ boolean makeFinalIsExplicit = false;
+ FieldDefaults fd = null;
+ for (JavacNode jn : typeNode.down()) {
+ if (jn.getKind() != Kind.ANNOTATION) continue;
+ JCAnnotation ann = (JCAnnotation) jn.get();
+ JCTree typeTree = ann.annotationType;
+ if (typeTree == null) continue;
+ String typeTreeToString = typeTree.toString();
+ if (!typeTreeToString.equals("FieldDefaults") && !typeTreeToString.equals("lombok.experimental.FieldDefaults")) continue;
+ if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue;
+
+ source = jn;
+ fieldDefaults = createAnnotation(FieldDefaults.class, jn);
+ levelIsExplicit = fieldDefaults.isExplicit("level");
+ makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
+
+ handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+
+ fd = fieldDefaults.getInstance();
+ if (!levelIsExplicit && !makeFinalIsExplicit) {
+ jn.addError("This does nothing; provide either level or makeFinal or both.");
+ }
+
+ if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
+ jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
+ levelIsExplicit = false;
+ }
+
+ deleteAnnotationIfNeccessary(jn, FieldDefaults.class);
+ deleteImportFromCompilationUnit(jn, "lombok.AccessLevel");
+ break;
}
- if (level == AccessLevel.PACKAGE) {
- annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
- }
+ if (fd == null && (type.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0) return;
- if (!makeFinal && annotation.isExplicit("makeFinal")) {
- annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
- }
+ boolean defaultToPrivate = levelIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
+ boolean defaultToFinal = makeFinalIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
- if (node == null) return;
+ if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return;
+ AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
+ boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
- generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
+ generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
}
}
diff --git a/src/core/lombok/javac/handlers/HandleHelper.java b/src/core/lombok/javac/handlers/HandleHelper.java
new file mode 100644
index 00000000..99131f70
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleHelper.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 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.core.handlers.HandlerUtil.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.TreeVisitor;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.experimental.Helper;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+@ProviderFor(JavacAnnotationHandler.class)
+public class HandleHelper extends JavacAnnotationHandler<Helper> {
+ @Override public void handle(AnnotationValues<Helper> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper");
+
+ deleteAnnotationIfNeccessary(annotationNode, Helper.class);
+ JavacNode annotatedType = annotationNode.up();
+ JavacNode containingMethod = annotatedType == null ? null : annotatedType.up();
+
+ if (annotatedType == null || containingMethod == null || annotatedType.getKind() != Kind.TYPE || containingMethod.getKind() != Kind.METHOD) {
+ annotationNode.addError("@Helper is legal only on method-local classes.");
+ return;
+ }
+
+ JCClassDecl annotatedType_ = (JCClassDecl) annotatedType.get();
+ JCMethodDecl amd = (JCMethodDecl) containingMethod.get();
+ List<JCStatement> origStatements = amd.body.stats;
+ Iterator<JCStatement> it = origStatements.iterator();
+ while (it.hasNext()) {
+ if (it.next() == annotatedType_) {
+ break;
+ }
+ }
+
+ java.util.List<String> knownMethodNames = new ArrayList<String>();
+
+ for (JavacNode ch : annotatedType.down()) {
+ if (ch.getKind() != Kind.METHOD) continue;
+ String n = ch.getName();
+ if (n == null || n.isEmpty() || n.charAt(0) == '<') continue;
+ knownMethodNames.add(n);
+ }
+
+ Collections.sort(knownMethodNames);
+ final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]);
+
+ final Name helperName = annotationNode.toName("$" + annotatedType_.name);
+ final boolean[] helperUsed = new boolean[1];
+ final JavacTreeMaker maker = annotationNode.getTreeMaker();
+
+ TreeVisitor<Void, Void> visitor = new TreeScanner<Void, Void>() {
+ @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
+ JCMethodInvocation jcmi = (JCMethodInvocation) node;
+ apply(jcmi);
+ return super.visitMethodInvocation(node, p);
+ }
+
+ private void apply(JCMethodInvocation jcmi) {
+ if (!(jcmi.meth instanceof JCIdent)) return;
+ JCIdent jci = (JCIdent) jcmi.meth;
+ if (Arrays.binarySearch(knownMethodNames_, jci.name.toString()) < 0) return;
+ jcmi.meth = maker.Select(maker.Ident(helperName), jci.name);
+ helperUsed[0] = true;
+ }
+ };
+
+ while (it.hasNext()) {
+ JCStatement stat = it.next();
+ stat.accept(visitor, null);
+ }
+
+ if (!helperUsed[0]) {
+ annotationNode.addWarning("No methods of this helper class are ever used.");
+ return;
+ }
+
+ ListBuffer<JCStatement> newStatements = new ListBuffer<JCStatement>();
+
+ boolean mark = false;
+ for (JCStatement stat : origStatements) {
+ newStatements.append(stat);
+ if (mark || stat != annotatedType_) continue;
+ mark = true;
+ JCExpression init = maker.NewClass(null, List.<JCExpression>nil(), maker.Ident(annotatedType_.name), List.<JCExpression>nil(), null);
+ JCExpression varType = maker.Ident(annotatedType_.name);
+ JCVariableDecl decl = maker.VarDef(maker.Modifiers(Flags.FINAL), helperName, varType, init);
+ newStatements.append(decl);
+ }
+ amd.body.stats = newStatements.toList();
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java
index cd8e3402..81aa1525 100644
--- a/src/core/lombok/javac/handlers/HandleNonNull.java
+++ b/src/core/lombok/javac/handlers/HandleNonNull.java
@@ -85,7 +85,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
}
if (declaration.body == null) {
- annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method.");
+ // This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7
return;
}
@@ -141,6 +141,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
List<JCStatement> newList = tail.prepend(nullCheck);
for (JCStatement stat : head) newList = newList.prepend(stat);
declaration.body.stats = newList;
+ annotationNode.getAst().setChanged();
}
public boolean isNullCheck(JCStatement stat) {
diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java
index a4f8cb45..010c05a5 100644
--- a/src/core/lombok/javac/handlers/HandleUtilityClass.java
+++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java
@@ -52,7 +52,7 @@ import com.sun.tools.javac.util.Name;
@ProviderFor(JavacAnnotationHandler.class)
public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> {
@Override public void handle(AnnotationValues<UtilityClass> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTLITY_CLASS_FLAG_USAGE, "@UtilityClass");
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass");
deleteAnnotationIfNeccessary(annotationNode, UtilityClass.class);
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 9eadd750..337ab2d7 100644
--- a/src/core/lombok/javac/handlers/HandleVal.java
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -51,11 +51,11 @@ import com.sun.tools.javac.util.List;
@ResolutionResetNeeded
public class HandleVal extends JavacASTAdapter {
@Override public void visitLocal(JavacNode localNode, JCVariableDecl local) {
- if (local.vartype == null || (!local.vartype.toString().equals("val") && !local.vartype.toString().equals("lombok.val"))) return;
-
- JCTree source = local.vartype;
-
- if (!typeMatches(val.class, localNode, local.vartype)) return;
+ JCTree typeTree = local.vartype;
+ if (typeTree == null) return;
+ String typeTreeToString = typeTree.toString();
+ if (!typeTreeToString.equals("val") && !typeTreeToString.equals("lombok.val")) return;
+ if (!typeMatches(val.class, localNode, typeTree)) return;
handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
@@ -88,7 +88,7 @@ public class HandleVal extends JavacASTAdapter {
local.mods.flags |= Flags.FINAL;
if (!localNode.shouldDeleteLombokAnnotations()) {
- JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), source, localNode.getContext());
+ JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext());
local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation);
}
@@ -156,7 +156,7 @@ public class HandleVal extends JavacASTAdapter {
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
throw e;
} finally {
- recursiveSetGeneratedBy(local.vartype, source, localNode.getContext());
+ recursiveSetGeneratedBy(local.vartype, typeTree, localNode.getContext());
}
}
}
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 0db59da1..4e9be00b 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -21,6 +21,7 @@
*/
package lombok.javac.handlers;
+import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacAugments.JCTree_generatedNode;
@@ -840,14 +841,18 @@ public class JavacHandlerUtil {
List<JCTree> insertAfter = null;
List<JCTree> insertBefore = type.defs;
- while (insertBefore.tail != null) {
+ while (true) {
+ boolean skip = false;
if (insertBefore.head instanceof JCVariableDecl) {
JCVariableDecl f = (JCVariableDecl) insertBefore.head;
- if (isEnumConstant(f) || isGenerated(f)) {
- insertAfter = insertBefore;
- insertBefore = insertBefore.tail;
- continue;
- }
+ if (isEnumConstant(f) || isGenerated(f)) skip = true;
+ } else if (insertBefore.head instanceof JCMethodDecl) {
+ if ((((JCMethodDecl) insertBefore.head).mods.flags & GENERATEDCONSTR) != 0) skip = true;
+ }
+ if (skip) {
+ insertAfter = insertBefore;
+ insertBefore = insertBefore.tail;
+ continue;
}
break;
}
@@ -1485,6 +1490,18 @@ public class JavacHandlerUtil {
} catch (Exception ignore) {}
}
+ private static final Pattern FIND_RETURN = Pattern.compile("^\\s*\\**\\s*@returns?\\s+.*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
+ static String addReturnsThisIfNeeded(String in) {
+ if (FIND_RETURN.matcher(in).find()) return in;
+
+ return addJavadocLine(in, "@return this");
+ }
+
+ static String addJavadocLine(String in, String line) {
+ if (in.endsWith("\n")) return in + line + "\n";
+ return in + "\n" + line;
+ }
+
private static class CopyJavadoc_8 {
static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode, Object dc) {
DocCommentTable dct = (DocCommentTable) dc;
@@ -1492,6 +1509,9 @@ public class JavacHandlerUtil {
if (javadoc != null) {
String[] filtered = copyMode.split(javadoc.getText());
+ if (copyMode == CopyJavadoc.SETTER && shouldReturnThis(from)) {
+ filtered[0] = addReturnsThisIfNeeded(filtered[0]);
+ }
dct.putComment(to, createJavadocComment(filtered[0], from));
dct.putComment(from.get(), createJavadocComment(filtered[1], from));
}
@@ -1525,6 +1545,9 @@ public class JavacHandlerUtil {
if (javadoc != null) {
String[] filtered = copyMode.split(javadoc);
+ if (copyMode == CopyJavadoc.SETTER && shouldReturnThis(from)) {
+ filtered[0] = addReturnsThisIfNeeded(filtered[0]);
+ }
docComments.put(to, filtered[0]);
docComments.put(from.get(), filtered[1]);
}
diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
index 7fca01ae..c6d601bd 100644
--- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -267,13 +267,15 @@ public class JavacSingularsRecipes {
}
/** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */
- protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard) {
+ protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard, boolean parens) {
Name thisName = builderType.toName("this");
JCExpression fn = maker.Select(maker.Select(maker.Ident(thisName), name), builderType.toName("size"));
JCExpression sizeInvoke = maker.Apply(List.<JCExpression>nil(), fn, List.<JCExpression>nil());
if (nullGuard) {
JCExpression isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(thisName), name), maker.Literal(CTC_BOT, 0));
- return maker.Conditional(isNull, maker.Literal(CTC_INT, 0), sizeInvoke);
+ JCExpression out = maker.Conditional(isNull, maker.Literal(CTC_INT, 0), sizeInvoke);
+ if (parens) return maker.Parens(out);
+ return out;
}
return sizeInvoke;
}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
index 9ec77e78..3002a98f 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
@@ -87,7 +87,7 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari
cases.append(defaultCase);
}
- JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true), cases.toList());
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false), cases.toList());
JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source);
JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
index f419a014..25669721 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
@@ -89,7 +89,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
cases.append(defaultCase);
}
- JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true), cases.toList());
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false), cases.toList());
JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source);
JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
@@ -136,10 +136,10 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
Name varName = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName();
// this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE;
// lessThanCutOff = this.varName.size() < MAX_POWER_OF_2
- JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard), maker.Literal(CTC_INT, 0x40000000));
+ JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 0x40000000));
JCExpression integerMaxValue = genJavaLangTypeRef(builderType, "Integer", "MAX_VALUE");
- JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard));
- JCExpression sizeFormulaRightLeft = maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard), maker.Literal(CTC_INT, 3));
+ JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true));
+ JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 3)));
JCExpression sizeFormulaRight = maker.Binary(CTC_DIV, sizeFormulaRightLeft, maker.Literal(CTC_INT, 3));
JCExpression sizeFormula = maker.Binary(CTC_PLUS, sizeFormulaLeft, sizeFormulaRight);
constructorArgs = List.<JCExpression>of(maker.Conditional(lessThanCutoff, sizeFormula, integerMaxValue));
@@ -167,7 +167,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar)));
JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2)));
JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0));
- JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard));
+ JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true));
JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar));
fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement);
} else {