mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-15 06:16:14 +00:00
@ToString, @Getter, and @XArgsConstructor now work on, and are legal on, enums. Docs have been updated.
Behaviour of @XArgsConstructor when its placement makes no sense (i.e. when annotating an interface with them) is no longer 'throw weird errors', but has been brought in line with the others: A nice error message is generated. Fixes issue #175
This commit is contained in:
1 parent
d80139fa8f
commit
52582dc10e
13 files changed
+86
-34
No files matched your search
@@ -5,6 +5,7 @@ Lombok Changelog
|
||||
* FEATURE: Added support for several logging frameworks by the `@Log` annotation.
|
||||
* FEATURE: Lombok now supports post-compile transformers. [Issue #144](http://code.google.com/p/projectlombok/issues/detail?id=144)
|
||||
* FEATURE: Using `@SneakyThrows` no longer requires a runtime dependency on lombok.jar. In fact, any call to {@code Lombok.sneakyThrows(ex)} is optimized at the bytecode level and no longer requires you to actually have lombok.jar or lombok-runtime.jar on the classpath.
|
||||
* FEATURE: @XArgsConstructor, @Getter, and @ToString can now be used on enum declarations. Previously, behaviour of these annotations on enums was undefined.
|
||||
* BUGFIX: `@Setter` and `@Getter` can now be applied to static fields again (was broken in v0.9.3 only). [Issue #136](http://code.google.com/p/projectlombok/issues/detail?id=136)
|
||||
* BUGFIX: delombok added type parameters to constructors that mirror the type's own type parameters. This resulted in delombok turning any generated constructor that takes at least 1 parameter of type 'T' into something that didn't compile, and to boot, a confusing error message ('T is not compatible with T'). This is now fixed. [Issue #140](http://code.google.com/p/projectlombok/issues/detail?id=140)
|
||||
* BUGFIX: Add null check for `@Cleanup` [Issue #154](http://code.google.com/p/projectlombok/issues/detail?id=154)
|
||||
|
||||
@@ -293,6 +293,24 @@ public class EclipseHandlerUtil {
|
||||
NOT_EXISTS, EXISTS_BY_USER, EXISTS_BY_LOMBOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the field should be included in operations that work on 'all' fields:
|
||||
* If the field is static, or starts with a '$', or is actually an enum constant, 'false' is returned, indicating you should skip it.
|
||||
*/
|
||||
public static boolean filterField(FieldDeclaration declaration) {
|
||||
// Skip the fake fields that represent enum constants.
|
||||
if (declaration.initialization instanceof AllocationExpression &&
|
||||
((AllocationExpression)declaration.initialization).enumConstant != null) return false;
|
||||
|
||||
// Skip fields that start with $
|
||||
if (declaration.name.length > 0 && declaration.name[0] == '$') return false;
|
||||
|
||||
// Skip static fields.
|
||||
if ((declaration.modifiers & ClassFileConstants.AccStatic) != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a field with the provided name.
|
||||
*
|
||||
|
||||
@@ -75,6 +75,7 @@ public class HandleConstructor {
|
||||
public static class HandleNoArgsConstructor implements EclipseAnnotationHandler<NoArgsConstructor> {
|
||||
@Override public boolean handle(AnnotationValues<NoArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
|
||||
EclipseNode typeNode = annotationNode.up();
|
||||
if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return true;
|
||||
NoArgsConstructor ann = annotation.getInstance();
|
||||
AccessLevel level = ann.access();
|
||||
String staticName = ann.staticName();
|
||||
@@ -89,6 +90,7 @@ public class HandleConstructor {
|
||||
public static class HandleRequiredArgsConstructor implements EclipseAnnotationHandler<RequiredArgsConstructor> {
|
||||
@Override public boolean handle(AnnotationValues<RequiredArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
|
||||
EclipseNode typeNode = annotationNode.up();
|
||||
if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return true;
|
||||
RequiredArgsConstructor ann = annotation.getInstance();
|
||||
AccessLevel level = ann.access();
|
||||
String staticName = ann.staticName();
|
||||
@@ -105,10 +107,7 @@ public class HandleConstructor {
|
||||
for (EclipseNode child : typeNode.down()) {
|
||||
if (child.getKind() != Kind.FIELD) continue;
|
||||
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
|
||||
//Skip fields that start with $
|
||||
if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue;
|
||||
//Skip static fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue;
|
||||
if (!EclipseHandlerUtil.filterField(fieldDecl)) continue;
|
||||
boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0;
|
||||
boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0;
|
||||
if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child);
|
||||
@@ -120,6 +119,7 @@ public class HandleConstructor {
|
||||
public static class HandleAllArgsConstructor implements EclipseAnnotationHandler<AllArgsConstructor> {
|
||||
@Override public boolean handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
|
||||
EclipseNode typeNode = annotationNode.up();
|
||||
if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return true;
|
||||
AllArgsConstructor ann = annotation.getInstance();
|
||||
AccessLevel level = ann.access();
|
||||
String staticName = ann.staticName();
|
||||
@@ -130,12 +130,11 @@ public class HandleConstructor {
|
||||
for (EclipseNode child : typeNode.down()) {
|
||||
if (child.getKind() != Kind.FIELD) continue;
|
||||
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
|
||||
// Skip fields that start with $
|
||||
if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue;
|
||||
// Skip static fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue;
|
||||
if (!EclipseHandlerUtil.filterField(fieldDecl)) continue;
|
||||
|
||||
// Skip initialized final fields.
|
||||
if (((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue;
|
||||
if (((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) return false;
|
||||
|
||||
fields.add(child);
|
||||
}
|
||||
new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, suppressConstructorProperties, ast);
|
||||
@@ -143,6 +142,20 @@ public class HandleConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
static boolean checkLegality(EclipseNode typeNode, EclipseNode errorNode, String name) {
|
||||
TypeDeclaration typeDecl = null;
|
||||
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
|
||||
int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
|
||||
boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
|
||||
|
||||
if (typeDecl == null || notAClass) {
|
||||
errorNode.addError(name + " is only supported on a class or an enum.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void generateRequiredArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, ASTNode source) {
|
||||
generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, source);
|
||||
}
|
||||
@@ -208,6 +221,10 @@ public class HandleConstructor {
|
||||
EclipseNode type, Collection<EclipseNode> fields, boolean suppressConstructorProperties, ASTNode source) {
|
||||
long p = (long)source.sourceStart << 32 | source.sourceEnd;
|
||||
|
||||
boolean isEnum = (((TypeDeclaration)type.get()).modifiers & ClassFileConstants.AccEnum) != 0;
|
||||
|
||||
if (isEnum) level = AccessLevel.PRIVATE;
|
||||
|
||||
ConstructorDeclaration constructor = new ConstructorDeclaration(
|
||||
((CompilationUnitDeclaration) type.top().get()).compilationResult);
|
||||
Eclipse.setGeneratedBy(constructor, source);
|
||||
|
||||
@@ -193,14 +193,12 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
|
||||
for (EclipseNode child : typeNode.down()) {
|
||||
if (child.getKind() != Kind.FIELD) continue;
|
||||
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
|
||||
//Skip static fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue;
|
||||
if (!EclipseHandlerUtil.filterField(fieldDecl)) continue;
|
||||
|
||||
//Skip transient fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0) continue;
|
||||
//Skip excluded fields.
|
||||
if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue;
|
||||
//Skip fields that start with $.
|
||||
if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue;
|
||||
nodesForEquality.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,10 +87,10 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
|
||||
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
|
||||
int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
|
||||
boolean notAClass = (modifiers &
|
||||
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0;
|
||||
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
|
||||
|
||||
if (typeDecl == null || notAClass) {
|
||||
pos.addError("@Getter is only supported on a class or a field.");
|
||||
pos.addError("@Getter is only supported on a class, an enum, or a field.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -103,11 +103,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
|
||||
public boolean fieldQualifiesForGetterGeneration(EclipseNode field) {
|
||||
if (field.getKind() != Kind.FIELD) return false;
|
||||
FieldDeclaration fieldDecl = (FieldDeclaration) field.get();
|
||||
//Skip fields that start with $
|
||||
if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') return false;
|
||||
//Skip static fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) return false;
|
||||
return true;
|
||||
return EclipseHandlerUtil.filterField(fieldDecl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,10 +85,8 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> {
|
||||
for (EclipseNode field : typeNode.down()) {
|
||||
if (field.getKind() != Kind.FIELD) continue;
|
||||
FieldDeclaration fieldDecl = (FieldDeclaration) field.get();
|
||||
//Skip fields that start with $
|
||||
if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue;
|
||||
//Skip static fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue;
|
||||
if (!EclipseHandlerUtil.filterField(fieldDecl)) continue;
|
||||
|
||||
//Skip final fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) continue;
|
||||
|
||||
|
||||
@@ -128,10 +128,10 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
|
||||
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
|
||||
int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
|
||||
boolean notAClass = (modifiers &
|
||||
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0;
|
||||
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
|
||||
|
||||
if (typeDecl == null || notAClass) {
|
||||
errorNode.addError("@ToString is only supported on a class.");
|
||||
errorNode.addError("@ToString is only supported on a class or enum.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -152,12 +152,11 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
|
||||
for (EclipseNode child : typeNode.down()) {
|
||||
if (child.getKind() != Kind.FIELD) continue;
|
||||
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
|
||||
//Skip static fields.
|
||||
if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue;
|
||||
if (!EclipseHandlerUtil.filterField(fieldDecl)) continue;
|
||||
|
||||
//Skip excluded fields.
|
||||
if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue;
|
||||
//Skip fields that start with $
|
||||
if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue;
|
||||
|
||||
nodesForToString.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public class HandleConstructor {
|
||||
markAnnotationAsProcessed(annotationNode, NoArgsConstructor.class);
|
||||
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
|
||||
JavacNode typeNode = annotationNode.up();
|
||||
if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return true;
|
||||
NoArgsConstructor ann = annotation.getInstance();
|
||||
AccessLevel level = ann.access();
|
||||
String staticName = ann.staticName();
|
||||
@@ -82,6 +83,7 @@ public class HandleConstructor {
|
||||
markAnnotationAsProcessed(annotationNode, RequiredArgsConstructor.class);
|
||||
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
|
||||
JavacNode typeNode = annotationNode.up();
|
||||
if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return true;
|
||||
RequiredArgsConstructor ann = annotation.getInstance();
|
||||
AccessLevel level = ann.access();
|
||||
String staticName = ann.staticName();
|
||||
@@ -120,6 +122,7 @@ public class HandleConstructor {
|
||||
markAnnotationAsProcessed(annotationNode, AllArgsConstructor.class);
|
||||
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
|
||||
JavacNode typeNode = annotationNode.up();
|
||||
if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return true;
|
||||
AllArgsConstructor ann = annotation.getInstance();
|
||||
AccessLevel level = ann.access();
|
||||
String staticName = ann.staticName();
|
||||
@@ -148,6 +151,20 @@ public class HandleConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
static boolean checkLegality(JavacNode typeNode, JavacNode errorNode, String name) {
|
||||
JCClassDecl typeDecl = null;
|
||||
if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get();
|
||||
long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags;
|
||||
boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0;
|
||||
|
||||
if (typeDecl == null || notAClass) {
|
||||
errorNode.addError(name + " is only supported on a class or an enum.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists) {
|
||||
generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false);
|
||||
}
|
||||
|
||||
@@ -85,10 +85,10 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> {
|
||||
JCClassDecl typeDecl = null;
|
||||
if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get();
|
||||
long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags;
|
||||
boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
|
||||
boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0;
|
||||
|
||||
if (typeDecl == null || notAClass) {
|
||||
errorNode.addError("@Getter is only supported on a class or a field.");
|
||||
errorNode.addError("@Getter is only supported on a class, an enum, or a field.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
|
||||
boolean notAClass = true;
|
||||
if (typeNode.get() instanceof JCClassDecl) {
|
||||
long flags = ((JCClassDecl)typeNode.get()).mods.flags;
|
||||
notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
|
||||
notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0;
|
||||
}
|
||||
|
||||
if (callSuper == null) {
|
||||
@@ -127,7 +127,7 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
|
||||
}
|
||||
|
||||
if (notAClass) {
|
||||
errorNode.addError("@ToString is only supported on a class.");
|
||||
errorNode.addError("@ToString is only supported on a class or enum.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
The <code>@java.beans.ConstructorProperties</code> annotation is never generated for a constructor with no arguments. This also explains why <code>@NoArgsConstructor</code>
|
||||
lacks the <code>suppressConstructorProperties</code> annotation method. The <code>@ConstructorProperties</code> annotation is also omitted for private constructors. The
|
||||
generated static factory methods also do not get <code>@ConstructorProperties</code>, as this annotation can only be added to real constructors.
|
||||
</p><p>
|
||||
<code>@XArgsConstructor</code> can also be used on an enum definition. The generated constructor will always be
|
||||
private, because non-private constructors aren't legal in enums. You don't have to specify <code>AccessLevel.PRIVATE</code>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -67,6 +67,9 @@
|
||||
</p><p>
|
||||
Using the <code>AccessLevel.NONE</code> access level simply generates nothing. It's useful only in combination with
|
||||
<a href="Data.html"><code>@Data</code></a> or a class-wide <code>@Getter</code> or <code>@Setter</code>.
|
||||
</p><p>
|
||||
<code>@Getter</code> can also be used on enums. <code>@Setter</code> can't, not for a technical reason, but
|
||||
for a pragmatic one: Setters on enums are an extremely bad idea.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -62,6 +62,8 @@
|
||||
</p><p>
|
||||
If a getter exists for a field to be included, it is called instead of using a direct field reference. This behaviour can be suppressed:<br />
|
||||
<code>@ToString(doNotUseGetters = true)</code>
|
||||
</p><p>
|
||||
<code>@ToString</code> can also be used on an enum definition.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user