From 334580ee548ddb09e34564183aba5008fdb29a22 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 22 Jul 2015 04:32:30 +0200 Subject: [trivial] restyle. --- .../lombok/eclipse/handlers/HandleBuilder.java | 8 +-- .../javac/handlers/HandleEqualsAndHashCode.java | 78 +++++++++++----------- 2 files changed, 43 insertions(+), 43 deletions(-) (limited to 'src/core/lombok') diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index c1b0d8a3..d011e2b5 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -335,8 +335,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { } public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { - MethodDeclaration out = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List statements = new ArrayList(); @@ -405,8 +404,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; - MethodDeclaration out = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.selector = builderMethodName.toCharArray(); out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -476,7 +474,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); MethodDeclaration setter = HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic, - sourceNode, Collections.emptyList(), Collections.emptyList()); + sourceNode, Collections.emptyList(), Collections.emptyList()); injectMethod(builderType, setter); } diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index c36b0016..b8afce8e 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 excludes, List includes, - Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List onParam) { + Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List 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 JavacAnnotationHandlernil(), - maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), - List.nil()); + maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), + List.nil()); statements.append(createResultCalculation(typeNode, callToSuper)); } @@ -258,14 +259,14 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), - genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"), - List.of(fieldAccessor)))); + List.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.nil(), - genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"), - List.of(fieldAccessor)); + List.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,8 +300,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")), - List.nil()); + List.nil()); JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(dollarFieldName), maker.Literal(CTC_BOT, null)); statements.append(createResultCalculation(typeNode, maker.Conditional(thisEqualsNull, maker.Literal(HandlerUtil.primeForNull()), hcCall))); } @@ -325,7 +326,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), List.nil(), List.nil(), body, null), source, typeNode.getContext()); + List.nil(), List.nil(), List.nil(), body, null), source, typeNode.getContext()); } public JCExpressionStatement createResultCalculation(JavacNode typeNode, JCExpression expr) { @@ -339,7 +340,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler>> 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); @@ -385,7 +386,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), - maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")), - List.of(maker.Ident(oName))); + maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")), + List.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 args = List.of(thisFieldAccessor, otherFieldAccessor); statements.append(maker.If(maker.Unary(CTC_NOT, - maker.Apply(List.nil(), eqMethod, args)), returnBool(maker, false), null)); + maker.Apply(List.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; + Name fieldName = ((JCVariableDecl) fieldNode.get()).name; Name thisDollarFieldName = thisDollar.append(fieldName); Name otherDollarFieldName = otherDollar.append(fieldName); @@ -484,8 +485,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil(), - maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")), - List.of(maker.Ident(otherDollarFieldName))); + maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")), + List.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 args = List.of(thisDotField, otherDotField); JCBinary compareCallEquals0 = maker.Binary(CTC_NOT_EQUAL, maker.Apply( - List.nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0)); + List.nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0)); return maker.If(compareCallEquals0, returnBool(maker, false), null); } -- cgit From fd1d18a29957fc1999b7dd5725641662ad3bd887 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 22 Jul 2015 04:40:19 +0200 Subject: Support for @NoArgsConstructor(force = true). --- doc/changelog.markdown | 1 + src/core/lombok/NoArgsConstructor.java | 8 +- .../lombok/eclipse/handlers/HandleBuilder.java | 8 +- .../lombok/eclipse/handlers/HandleConstructor.java | 134 +++++++++++++-------- src/core/lombok/javac/handlers/HandleBuilder.java | 4 +- .../lombok/javac/handlers/HandleConstructor.java | 91 +++++++++----- .../resource/after-delombok/Constructors.java | 19 +++ .../transform/resource/after-ecj/Constructors.java | 18 +++ test/transform/resource/before/Constructors.java | 11 +- website/features/Constructor.html | 3 +- 10 files changed, 211 insertions(+), 86 deletions(-) (limited to 'src/core/lombok') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index ae8b07ab..d1fcb777 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,6 +2,7 @@ Lombok Changelog ---------------- ### v1.16.5 "Edgy Guinea Pig" +* FEATURE: `@NoArgsConstructor(force = true)` can be used to create no args constructors even if final fields are present. * BUGFIX: Parameterized static methods with `@Builder` would produce compiler errors in javac. [Issue #828](https://github.com/rzwitserloot/lombok/issues/828). * PERFORMANCE: the config system caused significant slowdowns in eclipse if the filesystem is very slow (network file system) or has a slow authentication system. * FEATURE: the `hashCode()` method generated by lombok via `@EqualsAndHashCode`, `@Data`, and `@Value` is now smarter about nulls; they are treated as if they hash to a magic prime instead of 0, which reduces hash collisions. diff --git a/src/core/lombok/NoArgsConstructor.java b/src/core/lombok/NoArgsConstructor.java index ef347273..ff437bba 100644 --- a/src/core/lombok/NoArgsConstructor.java +++ b/src/core/lombok/NoArgsConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-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 @@ -60,6 +60,12 @@ public @interface NoArgsConstructor { */ AccessLevel access() default lombok.AccessLevel.PUBLIC; + /** + * If {@code true}, initializes all final fields to 0 / null / false. + * Otherwise, a compile time error occurs. + */ + boolean force() default false; + /** * Placeholder annotation to enable the placement of annotations on the generated code. * @deprecated Don't use this annotation, ever - Read the documentation. diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index d011e2b5..5acd9249 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -160,8 +160,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { allFields.add(fieldNode); } - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, null, SkipIfConstructorExists.I_AM_BUILDER, null, - Collections.emptyList(), annotationNode); + new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, null, + Collections.emptyList(), annotationNode); returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; @@ -278,8 +278,8 @@ public class HandleBuilder extends EclipseAnnotationHandler { if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration cd = HandleConstructor.createConstructor( - AccessLevel.PACKAGE, builderType, Collections.emptyList(), null, - annotationNode, Collections.emptyList()); + AccessLevel.PACKAGE, builderType, Collections.emptyList(), false, null, + annotationNode, Collections.emptyList()); if (cd != null) injectMethod(builderType, cd); } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index 5bcc803a..85d1d4ed 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-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 @@ -29,6 +29,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import lombok.AccessLevel; @@ -48,13 +49,20 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.Assignment; +import org.eclipse.jdt.internal.compiler.ast.CharLiteral; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.DoubleLiteral; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; 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.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; @@ -63,7 +71,9 @@ import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.mangosdk.spi.ProviderFor; public class HandleConstructor { @@ -78,11 +88,13 @@ public class HandleConstructor { AccessLevel level = ann.access(); String staticName = ann.staticName(); if (level == AccessLevel.NONE) return; - List fields = new ArrayList(); + boolean force = ann.force(); + + List fields = force ? findFinalFields(typeNode) : Collections.emptyList(); List onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode); - new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode); + new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode); } } @@ -107,19 +119,27 @@ public class HandleConstructor { List onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode); new HandleConstructor().generateConstructor( - typeNode, level, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, - suppressConstructorProperties, onConstructor, annotationNode); + typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, + suppressConstructorProperties, onConstructor, annotationNode); } } private static List findRequiredFields(EclipseNode typeNode) { + return findFields(typeNode, true); + } + + private static List findFinalFields(EclipseNode typeNode) { + return findFields(typeNode, false); + } + + private static List findFields(EclipseNode typeNode, boolean nullMarked) { List fields = new ArrayList(); for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); if (!filterField(fieldDecl)) continue; boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; - boolean isNonNull = findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0; + boolean isNonNull = nullMarked && findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0; if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child); } return fields; @@ -161,8 +181,8 @@ public class HandleConstructor { List onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode); new HandleConstructor().generateConstructor( - typeNode, level, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, - suppressConstructorProperties, onConstructor, annotationNode); + typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, + suppressConstructorProperties, onConstructor, annotationNode); } } @@ -184,14 +204,14 @@ public class HandleConstructor { EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, null, onConstructor, sourceNode); + generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode); } public void generateAllArgsConstructor( EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List onConstructor, EclipseNode sourceNode) { - generateConstructor(typeNode, level, findAllFields(typeNode), staticName, skipIfConstructorExists, null, onConstructor, sourceNode); + generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode); } public enum SkipIfConstructorExists { @@ -199,8 +219,8 @@ public class HandleConstructor { } public void generateConstructor( - EclipseNode typeNode, AccessLevel level, List fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, - Boolean suppressConstructorProperties, List onConstructor, EclipseNode sourceNode) { + EclipseNode typeNode, AccessLevel level, List fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, + Boolean suppressConstructorProperties, List onConstructor, EclipseNode sourceNode) { ASTNode source = sourceNode.get(); boolean staticConstrRequired = staticName != null && !staticName.equals(""); @@ -210,8 +230,8 @@ public class HandleConstructor { for (EclipseNode 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); @@ -224,8 +244,8 @@ public class HandleConstructor { // the 'staticName' parameter of the @XArgsConstructor you've stuck on your type. // We should warn that we're ignoring @Data's 'staticConstructor' param. typeNode.addWarning( - "Ignoring static constructor name: explicit @XxxArgsConstructor annotation present; its `staticName` parameter will be used.", - source.sourceStart, source.sourceEnd); + "Ignoring static constructor name: explicit @XxxArgsConstructor annotation present; its `staticName` parameter will be used.", + source.sourceStart, source.sourceEnd); } return; } @@ -234,11 +254,11 @@ public class HandleConstructor { } ConstructorDeclaration constr = createConstructor( - staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, - suppressConstructorProperties, sourceNode, onConstructor); + staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault, + suppressConstructorProperties, sourceNode, onConstructor); injectMethod(typeNode, constr); if (staticConstrRequired) { - MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, fields, source); + MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.emptyList() : fields, source); injectMethod(typeNode, staticConstr); } } @@ -248,7 +268,7 @@ public class HandleConstructor { if (fields.isEmpty()) return null; int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; + long p = (long) pS << 32 | pE; long[] poss = new long[3]; Arrays.fill(poss, p); QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(JAVA_BEANS_CONSTRUCTORPROPERTIES, poss); @@ -276,14 +296,14 @@ public class HandleConstructor { } public static ConstructorDeclaration createConstructor( - AccessLevel level, EclipseNode type, Collection fields, - Boolean suppressConstructorProperties, EclipseNode sourceNode, List onConstructor) { + AccessLevel level, EclipseNode type, Collection fields, boolean allToDefault, + Boolean suppressConstructorProperties, EclipseNode sourceNode, List onConstructor) { ASTNode source = sourceNode.get(); - TypeDeclaration typeDeclaration = ((TypeDeclaration)type.get()); - long p = (long)source.sourceStart << 32 | source.sourceEnd; + TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get()); + long p = (long) source.sourceStart << 32 | source.sourceEnd; - boolean isEnum = (((TypeDeclaration)type.get()).modifiers & ClassFileConstants.AccEnum) != 0; + boolean isEnum = (((TypeDeclaration) type.get()).modifiers & ClassFileConstants.AccEnum) != 0; if (isEnum) level = AccessLevel.PRIVATE; @@ -295,8 +315,7 @@ public class HandleConstructor { } } - ConstructorDeclaration constructor = new ConstructorDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); constructor.modifiers = toEclipseModifier(level); constructor.selector = typeDeclaration.name; @@ -319,22 +338,27 @@ public class HandleConstructor { char[] rawName = field.name; char[] fieldName = removePrefixFromField(fieldNode); FieldReference thisX = new FieldReference(rawName, p); - thisX.receiver = new ThisReference((int)(p >> 32), (int)p); + int s = (int) (p >> 32); + int e = (int) p; + thisX.receiver = new ThisReference(s, e); + + Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p); - SingleNameReference assignmentNameRef = new SingleNameReference(fieldName, p); - Assignment assignment = new Assignment(thisX, assignmentNameRef, (int)p); - assignment.sourceStart = (int)(p >> 32); assignment.sourceEnd = assignment.statementEnd = (int)(p >> 32); + Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p); + assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32); assigns.add(assignment); - 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(field, sourceNode); - if (nullCheck != null) nullChecks.add(nullCheck); + 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(field, sourceNode); + if (nullCheck != null) nullChecks.add(nullCheck); + } + parameter.annotations = copyAnnotations(source, nonNulls, nullables); + params.add(parameter); } - parameter.annotations = copyAnnotations(source, nonNulls, nullables); - params.add(parameter); } nullChecks.addAll(assigns); @@ -343,19 +367,32 @@ public class HandleConstructor { /* Generate annotations that must be put on the generated method, and attach them. */ { Annotation[] constructorProperties = null; - if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) { + if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) { constructorProperties = createConstructorProperties(source, fields); } constructor.annotations = copyAnnotations(source, - onConstructor.toArray(new Annotation[0]), - constructorProperties); + onConstructor.toArray(new Annotation[0]), + constructorProperties); } constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); return constructor; } + private static Expression getDefaultExpr(TypeReference type, int s, int e) { + char[] lastToken = type.getLastToken(); + if (Arrays.equals(TypeConstants.BOOLEAN, lastToken)) return new FalseLiteral(s, e); + if (Arrays.equals(TypeConstants.CHAR, lastToken)) return new CharLiteral(new char[] {'\'', '\\', '0', '\''}, s, e); + if (Arrays.equals(TypeConstants.BYTE, lastToken) || Arrays.equals(TypeConstants.SHORT, lastToken) || + Arrays.equals(TypeConstants.INT, lastToken)) return IntLiteral.buildIntLiteral(new char[] {'0'}, s, e); + if (Arrays.equals(TypeConstants.LONG, lastToken)) return LongLiteral.buildLongLiteral(new char[] {'0', 'L'}, s, e); + if (Arrays.equals(TypeConstants.FLOAT, lastToken)) return new FloatLiteral(new char[] {'0', 'F'}, s, e); + if (Arrays.equals(TypeConstants.DOUBLE, lastToken)) return new DoubleLiteral(new char[] {'0', 'D'}, s, e); + + return new NullLiteral(s, e); + } + public static boolean isLocalType(EclipseNode type) { Kind kind = type.up().getKind(); if (kind == Kind.COMPILATION_UNIT) return false; @@ -365,10 +402,9 @@ public class HandleConstructor { public MethodDeclaration createStaticConstructor(AccessLevel level, String name, EclipseNode type, Collection fields, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; + long p = (long) pS << 32 | pE; - MethodDeclaration constructor = new MethodDeclaration( - ((CompilationUnitDeclaration) type.top().get()).compilationResult); + MethodDeclaration constructor = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); constructor.modifiers = toEclipseModifier(level) | ClassFileConstants.AccStatic; TypeDeclaration typeDecl = (TypeDeclaration) type.get(); @@ -376,7 +412,7 @@ public class HandleConstructor { constructor.annotations = null; constructor.selector = name.toCharArray(); constructor.thrownExceptions = null; - constructor.typeParameters = copyTypeParams(((TypeDeclaration)type.get()).typeParameters, source); + constructor.typeParameters = copyTypeParams(((TypeDeclaration) type.get()).typeParameters, source); constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; @@ -389,7 +425,7 @@ public class HandleConstructor { for (EclipseNode fieldNode : fields) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); - long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd; + long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd; SingleNameReference nameRef = new SingleNameReference(field.name, fieldPos); assigns.add(nameRef); @@ -400,7 +436,7 @@ public class HandleConstructor { statement.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]); constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]); - constructor.statements = new Statement[] { new ReturnStatement(statement, (int)(p >> 32), (int)p) }; + constructor.statements = new Statement[] { new ReturnStatement(statement, (int) (p >> 32), (int)p) }; constructor.traverse(new SetGeneratedByVisitor(source), typeDecl.scope); return constructor; diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 7ef7b859..066e371e 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -142,7 +142,7 @@ public class HandleBuilder extends JavacAnnotationHandler { allFields.append(fieldNode); } - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode); + new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; @@ -255,7 +255,7 @@ public class HandleBuilder extends JavacAnnotationHandler { } if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.nil(), builderType, List.nil(), null, annotationNode); + JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.nil(), builderType, List.nil(), false, null, annotationNode); if (cd != null) injectMethod(builderType, cd); } 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 fields = List.nil(); - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, SkipIfConstructorExists.NO, null, annotationNode); + boolean force = ann.force(); + List fields = force ? findFinalFields(typeNode) : List.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 findRequiredFields(JavacNode typeNode) { + return findFields(typeNode, true); + } + + public static List findFinalFields(JavacNode typeNode) { + return findFields(typeNode, false); + } + + public static List findFields(JavacNode typeNode, boolean nullMarked) { ListBuffer fields = new ListBuffer(); 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.nil(), findRequiredFields(typeNode), staticName, skipIfConstructorExists, null, source); + generateConstructor(typeNode, level, List.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.nil(), findAllFields(typeNode), staticName, skipIfConstructorExists, null, source); + generateConstructor(typeNode, level, List.nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, source); } - public void generateConstructor(JavacNode typeNode, AccessLevel level, List onConstructor, List fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source) { + public void generateConstructor(JavacNode typeNode, AccessLevel level, List onConstructor, List 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.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 onConstructor, JavacNode typeNode, List fields, Boolean suppressConstructorProperties, JavacNode source) { + public static JCMethodDecl createConstructor(AccessLevel level, List onConstructor, JavacNode typeNode, List 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 nonNulls = findAnnotations(fieldNode, NON_NULL_PATTERN); - List 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 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.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(""), - null, List.nil(), params.toList(), List.nil(), - maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext()); + null, List.nil(), params.toList(), List.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/test/transform/resource/after-delombok/Constructors.java b/test/transform/resource/after-delombok/Constructors.java index 466ae9ee..c138a00c 100644 --- a/test/transform/resource/after-delombok/Constructors.java +++ b/test/transform/resource/after-delombok/Constructors.java @@ -98,3 +98,22 @@ class AllArgsConstructorPackageAccess { this.x = x; } } +class NoArgsConstructor2 { + final int x; + final double y; + final char c; + final boolean b; + final float f; + final String s; + byte z; + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public NoArgsConstructor2() { + this.x = 0; + this.y = 0.0; + this.c = '\000'; + this.b = false; + this.f = 0.0F; + this.s = null; + } +} diff --git a/test/transform/resource/after-ecj/Constructors.java b/test/transform/resource/after-ecj/Constructors.java index 998872a2..4cc24a81 100644 --- a/test/transform/resource/after-ecj/Constructors.java +++ b/test/transform/resource/after-ecj/Constructors.java @@ -77,4 +77,22 @@ super(); this.x = x; } +} +@lombok.NoArgsConstructor(force = true) class NoArgsConstructor2 { + final int x; + final double y; + final char c; + final boolean b; + final float f; + final String s; + byte z; + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") NoArgsConstructor2() { + super(); + this.x = 0; + this.y = 0D; + this.c = '\0'; + this.b = false; + this.f = 0F; + this.s = null; + } } \ No newline at end of file diff --git a/test/transform/resource/before/Constructors.java b/test/transform/resource/before/Constructors.java index c80acf2b..3569986f 100644 --- a/test/transform/resource/before/Constructors.java +++ b/test/transform/resource/before/Constructors.java @@ -32,4 +32,13 @@ } @lombok.AllArgsConstructor(access=lombok.AccessLevel.PACKAGE) class AllArgsConstructorPackageAccess { final String x; -} \ No newline at end of file +} +@lombok.NoArgsConstructor(force = true) class NoArgsConstructor2 { + final int x; + final double y; + final char c; + final boolean b; + final float f; + final String s; + byte z; +} diff --git a/website/features/Constructor.html b/website/features/Constructor.html index cbb76fcf..d7168372 100644 --- a/website/features/Constructor.html +++ b/website/features/Constructor.html @@ -17,8 +17,7 @@

This set of 3 annotations generate a constructor that will accept 1 parameter for certain fields, and simply assigns this parameter to the field.

- @NoArgsConstructor will generate a constructor with no parameters. If this is not possible (because of final fields), a compiler error will result instead. - For fields with constraints, such as @NonNull fields, no check or assignment is generated, so be aware that these constraints may then not be + @NoArgsConstructor will generate a constructor with no parameters. If this is not possible (because of final fields), a compiler error will result instead, unless @NoArgsConstructor(force = true) is used, then all final fields are initialized with 0 / false / null. For fields with constraints, such as @NonNull fields, no check or assignment is generated, so be aware that these constraints may then not be fulfilled until those fields are properly initialized later. Certain java constructs, such as hibernate and the Service Provider Interface require a no-args constructor. This annotation is useful primarily in combination with either @Data or one of the other constructor generating annotations.

-- cgit