diff options
35 files changed, 488 insertions, 111 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown index eba0c595..6fc3af38 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -4,6 +4,7 @@ Lombok Changelog ### v1.16.15 "Edgy Guinea Pig" * v1.16.14 is the latest stable release of Project Lombok. * PLATFORM: Improved jdk9 support, work in progress. +* BUGFIX: The `onX` feature (which lets you add annotations to generated methods) did not work if the annotation you added contained named parameters, and you are compiling with JDK8's javac. We can't fix this (it's a bug in javac), but we have provided an alternate, prettier way to do `onX` on javac8+. [Issue #778](https://github.com/rzwitserloot/lombok/issues/778) [onX documentation](https://projectlombok.org/features/experimental/onX.html) ### v1.16.14 (February 10th, 2017) * FEATURE: Generated classes, methods and fields can now also annotated with `@lombok.Generated` [Issue #1014](https://github.com/rzwitserloot/lombok/issues/1014)<br> diff --git a/src/core/lombok/AllArgsConstructor.java b/src/core/lombok/AllArgsConstructor.java index 3037c9db..45ae897c 100644 --- a/src/core/lombok/AllArgsConstructor.java +++ b/src/core/lombok/AllArgsConstructor.java @@ -49,7 +49,12 @@ public @interface AllArgsConstructor { String staticName() default ""; /** - * Any annotations listed here are put on the generated constructor. The syntax for this feature is: {@code @AllArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated constructor. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @AllArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @AllArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}. */ AnyAnnotation[] onConstructor() default {}; diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java index 605815ed..34f4ffc6 100644 --- a/src/core/lombok/EqualsAndHashCode.java +++ b/src/core/lombok/EqualsAndHashCode.java @@ -63,8 +63,13 @@ public @interface EqualsAndHashCode { boolean doNotUseGetters() default false; /** - * Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}. The syntax for this feature is: {@code @EqualsAndHashCode(onParam=@__({@AnnotationsGoHere}))} - * This is useful to add for example a {@code Nullable} annotation. + * Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}. + * This is useful to add for example a {@code Nullable} annotation.<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @EqualsAndHashCode(onParam=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @EqualsAndHashCode(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}. */ AnyAnnotation[] onParam() default {}; diff --git a/src/core/lombok/Getter.java b/src/core/lombok/Getter.java index 906ae60f..5a4056fa 100644 --- a/src/core/lombok/Getter.java +++ b/src/core/lombok/Getter.java @@ -58,9 +58,14 @@ public @interface Getter { lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; /** - * Any annotations listed here are put on the generated method. The syntax for this feature is: {@code @Getter(onMethod=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated method. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @Getter(onMethod=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @Getter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}. */ - AnyAnnotation[] onMethod() default @AnyAnnotation; + AnyAnnotation[] onMethod() default {}; boolean lazy() default false; diff --git a/src/core/lombok/NoArgsConstructor.java b/src/core/lombok/NoArgsConstructor.java index ff437bba..5f2268a8 100644 --- a/src/core/lombok/NoArgsConstructor.java +++ b/src/core/lombok/NoArgsConstructor.java @@ -51,7 +51,12 @@ public @interface NoArgsConstructor { String staticName() default ""; /** - * Any annotations listed here are put on the generated constructor. The syntax for this feature is: {@code @NoArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated constructor. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @NoArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @NoArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}. */ AnyAnnotation[] onConstructor() default {}; diff --git a/src/core/lombok/RequiredArgsConstructor.java b/src/core/lombok/RequiredArgsConstructor.java index 7a9da3f9..abfdd55c 100644 --- a/src/core/lombok/RequiredArgsConstructor.java +++ b/src/core/lombok/RequiredArgsConstructor.java @@ -49,7 +49,12 @@ public @interface RequiredArgsConstructor { String staticName() default ""; /** - * Any annotations listed here are put on the generated constructor. The syntax for this feature is: {@code @RequiredArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated constructor. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @RequiredArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @RequiredArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}. */ AnyAnnotation[] onConstructor() default {}; diff --git a/src/core/lombok/Setter.java b/src/core/lombok/Setter.java index ba9e9759..f2ebe5b3 100644 --- a/src/core/lombok/Setter.java +++ b/src/core/lombok/Setter.java @@ -59,12 +59,22 @@ public @interface Setter { lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; /** - * Any annotations listed here are put on the generated method. The syntax for this feature is: {@code @Setter(onMethod=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated method. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @Setter(onMethod=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @Setter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}. */ AnyAnnotation[] onMethod() default {}; /** - * Any annotations listed here are put on the generated method's parameter. The syntax for this feature is: {@code @Setter(onParam=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated method's parameter. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @Setter(onParam=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @Setter(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}. */ AnyAnnotation[] onParam() default {}; diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index ab4ca27b..4726b17e 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1616,6 +1616,14 @@ public class EclipseHandlerUtil { return true; } + public static void addError(String errorName, EclipseNode node) { + if (node.getLatestJavaSpecSupported() < 8) { + node.addError("The correct format is " + errorName + "_={@SomeAnnotation, @SomeOtherAnnotation})"); + } else { + node.addError("The correct format is " + errorName + "=@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + } + } + public static List<Annotation> unboxAndRemoveAnnotationParameter(Annotation annotation, String annotationName, String errorName, EclipseNode errorNode) { if ("value".equals(annotationName)) { // We can't unbox this, because SingleMemberAnnotation REQUIRES a value, and this method @@ -1638,51 +1646,70 @@ public class EclipseHandlerUtil { char[] nameAsCharArray = annotationName.toCharArray(); + top: for (int i = 0; i < pairs.length; i++) { - if (pairs[i].name == null || !Arrays.equals(nameAsCharArray, pairs[i].name)) continue; + boolean allowRaw; + char[] name = pairs[i].name; + if (name == null) continue; + if (name.length < nameAsCharArray.length) continue; + for (int j = 0; j < nameAsCharArray.length; j++) { + if (name[j] != nameAsCharArray[j]) continue top; + } + allowRaw = name.length > nameAsCharArray.length; + for (int j = nameAsCharArray.length; j < name.length; j++) { + if (name[j] != '_') continue top; + } + // If we're still here it's the targeted annotation param. Expression value = pairs[i].value; MemberValuePair[] newPairs = new MemberValuePair[pairs.length - 1]; if (i > 0) System.arraycopy(pairs, 0, newPairs, 0, i); if (i < pairs.length - 1) System.arraycopy(pairs, i + 1, newPairs, i, pairs.length - i - 1); normalAnnotation.memberValuePairs = newPairs; - // We have now removed the annotation parameter and stored '@__({... annotations ...})', - // which we must now unbox. - if (!(value instanceof Annotation)) { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); - return Collections.emptyList(); - } - - Annotation atDummyIdentifier = (Annotation) value; - if (!(atDummyIdentifier.type instanceof SingleTypeReference) || - !isAllValidOnXCharacters(((SingleTypeReference) atDummyIdentifier.type).token)) { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); - return Collections.emptyList(); - } - - if (atDummyIdentifier instanceof MarkerAnnotation) { - // It's @Getter(onMethod=@__). This is weird, but fine. - return Collections.emptyList(); - } + // We have now removed the annotation parameter and stored the value, + // which we must now unbox. It's either annotations, or @__(annotations). Expression content = null; - if (atDummyIdentifier instanceof NormalAnnotation) { - MemberValuePair[] mvps = ((NormalAnnotation) atDummyIdentifier).memberValuePairs; - if (mvps == null || mvps.length == 0) { - // It's @Getter(onMethod=@__()). This is weird, but fine. + if (value instanceof ArrayInitializer) { + if (!allowRaw) { + addError(errorName, errorNode); return Collections.emptyList(); } - if (mvps.length == 1 && Arrays.equals("value".toCharArray(), mvps[0].name)) { - content = mvps[0].value; + content = value; + } else if (!(value instanceof Annotation)) { + addError(errorName, errorNode); + return Collections.emptyList(); + } else { + Annotation atDummyIdentifier = (Annotation) value; + if (atDummyIdentifier.type instanceof SingleTypeReference && isAllValidOnXCharacters(((SingleTypeReference) atDummyIdentifier.type).token)) { + if (atDummyIdentifier instanceof MarkerAnnotation) { + return Collections.emptyList(); + } else if (atDummyIdentifier instanceof NormalAnnotation) { + MemberValuePair[] mvps = ((NormalAnnotation) atDummyIdentifier).memberValuePairs; + if (mvps == null || mvps.length == 0) { + return Collections.emptyList(); + } + if (mvps.length == 1 && Arrays.equals("value".toCharArray(), mvps[0].name)) { + content = mvps[0].value; + } + } else if (atDummyIdentifier instanceof SingleMemberAnnotation) { + content = ((SingleMemberAnnotation) atDummyIdentifier).memberValue; + } else { + addError(errorName, errorNode); + return Collections.emptyList(); + } + } else { + if (allowRaw) { + content = atDummyIdentifier; + } else { + addError(errorName, errorNode); + return Collections.emptyList(); + } } } - if (atDummyIdentifier instanceof SingleMemberAnnotation) { - content = ((SingleMemberAnnotation) atDummyIdentifier).memberValue; - } - if (content == null) { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + addError(errorName, errorNode); return Collections.emptyList(); } @@ -1694,13 +1721,13 @@ public class EclipseHandlerUtil { if (expressions != null) for (Expression ex : expressions) { if (ex instanceof Annotation) result.add((Annotation) ex); else { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + addError(errorName, errorNode); return Collections.emptyList(); } } return result; } else { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + addError(errorName, errorNode); return Collections.emptyList(); } } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index a3b0585d..2aed4a4e 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -93,7 +93,7 @@ public class HandleConstructor { boolean force = ann.force(); List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList(); - List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode); + List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode); new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode); } @@ -117,7 +117,7 @@ public class HandleConstructor { suppressConstructorProperties = suppress; } - List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode); + List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode); new HandleConstructor().generateConstructor( typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, @@ -179,7 +179,7 @@ public class HandleConstructor { suppressConstructorProperties = suppress; } - List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode); + List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor", annotationNode); new HandleConstructor().generateConstructor( typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index bc25ae2a..ceef3d3c 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -129,7 +129,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH List<String> includes = Arrays.asList(ann.of()); EclipseNode typeNode = annotationNode.up(); - List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam=", annotationNode); + List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode); checkForBogusFieldNames(typeNode, annotation); Boolean callSuper = ann.callSuper(); diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 77d678f2..c11303f3 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -148,7 +148,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { if (node == null) return; - List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod=", annotationNode); + List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode); switch (node.getKind()) { case FIELD: diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index 1fcf751d..7e7ea121 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -125,8 +125,8 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { AccessLevel level = annotation.getInstance().value(); if (level == AccessLevel.NONE || node == null) return; - List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode); - List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode); + List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod", annotationNode); + List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam", annotationNode); switch (node.getKind()) { case FIELD: diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java index d8ac8df6..200ebde7 100644 --- a/src/core/lombok/eclipse/handlers/HandleWither.java +++ b/src/core/lombok/eclipse/handlers/HandleWither.java @@ -127,8 +127,8 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> { AccessLevel level = annotation.getInstance().value(); if (level == AccessLevel.NONE || node == null) return; - List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod=", annotationNode); - List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam=", annotationNode); + List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod", annotationNode); + List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam", annotationNode); switch (node.getKind()) { case FIELD: diff --git a/src/core/lombok/experimental/Wither.java b/src/core/lombok/experimental/Wither.java index b4131187..3fb767d1 100644 --- a/src/core/lombok/experimental/Wither.java +++ b/src/core/lombok/experimental/Wither.java @@ -60,12 +60,22 @@ public @interface Wither { AccessLevel value() default AccessLevel.PUBLIC; /** - * Any annotations listed here are put on the generated method. The syntax for this feature is: {@code @Setter(onMethod=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated method. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @Wither(onMethod=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @Wither(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}. */ AnyAnnotation[] onMethod() default {}; /** - * Any annotations listed here are put on the generated method's parameter. The syntax for this feature is: {@code @Setter(onParam=@__({@AnnotationsGoHere}))} + * Any annotations listed here are put on the generated method's parameter. + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> + * up to JDK7:<br /> + * {@code @Wither(onParam=@__({@AnnotationsGoHere}))}<br /> + * from JDK8:<br /> + * {@code @Wither(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}. */ AnyAnnotation[] onParam() default {}; diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index ef4ba088..617ec0d1 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -71,7 +71,7 @@ public class HandleConstructor { deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return; - List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode); + List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode); NoArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); if (level == AccessLevel.NONE) return; @@ -91,7 +91,7 @@ public class HandleConstructor { deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return; - List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode); + List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode); RequiredArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); if (level == AccessLevel.NONE) return; @@ -141,7 +141,7 @@ public class HandleConstructor { deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return; - List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode); + List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor", annotationNode); AllArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); if (level == AccessLevel.NONE) return; diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index 8e868bca..6df56ed6 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -95,7 +95,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas List<String> excludes = List.from(ann.exclude()); List<String> includes = List.from(ann.of()); JavacNode typeNode = annotationNode.up(); - List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam=", annotationNode); + List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode); checkForBogusFieldNames(typeNode, annotation); Boolean callSuper = ann.callSuper(); diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 60dbe8ee..5a2e1993 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -147,7 +147,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { if (node == null) return; - List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod=", annotationNode); + List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode); switch (node.getKind()) { case FIELD: diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 02cc3775..e766127a 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -129,8 +129,8 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { if (level == AccessLevel.NONE || node == null) return; - List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode); - List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode); + List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod", annotationNode); + List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam", annotationNode); switch (node.getKind()) { case FIELD: diff --git a/src/core/lombok/javac/handlers/HandleWither.java b/src/core/lombok/javac/handlers/HandleWither.java index 8aec0240..987a3d34 100644 --- a/src/core/lombok/javac/handlers/HandleWither.java +++ b/src/core/lombok/javac/handlers/HandleWither.java @@ -131,8 +131,8 @@ public class HandleWither extends JavacAnnotationHandler<Wither> { if (level == AccessLevel.NONE || node == null) return; - List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod=", annotationNode); - List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam=", annotationNode); + List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod", annotationNode); + List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam", annotationNode); switch (node.getKind()) { case FIELD: diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 27289108..56e666f7 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1240,19 +1240,9 @@ public class JavacHandlerUtil { ListBuffer<JCExpression> params = new ListBuffer<JCExpression>(); ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>(); - try { - for (JCExpression arg : ast.args) { - String argName = "value"; - if (arg instanceof JCAssign) { - JCAssign as = (JCAssign) arg; - argName = as.lhs.toString(); - } - if (!argName.equals(parameterName)) continue; - } - } catch (Exception ignore) {} - outer: for (JCExpression param : ast.args) { + boolean allowRaw; String nameOfParam = "value"; JCExpression valueOfParam = null; if (param instanceof JCAssign) { @@ -1264,6 +1254,15 @@ public class JavacHandlerUtil { valueOfParam = assign.rhs; } + /* strip trailing underscores */ { + int lastIdx; + for (lastIdx = nameOfParam.length() ; lastIdx > 0; lastIdx--) { + if (nameOfParam.charAt(lastIdx - 1) != '_') break; + } + allowRaw = lastIdx < nameOfParam.length(); + nameOfParam = nameOfParam.substring(0, lastIdx); + } + if (!parameterName.equals(nameOfParam)) { params.append(param); continue outer; @@ -1276,48 +1275,68 @@ public class JavacHandlerUtil { String dummyAnnotationName = ((JCAnnotation) valueOfParam).annotationType.toString(); dummyAnnotationName = dummyAnnotationName.replace("_", "").replace("$", "").replace("x", "").replace("X", ""); if (dummyAnnotationName.length() > 0) { - annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); - continue outer; - } - for (JCExpression expr : ((JCAnnotation) valueOfParam).args) { - if (expr instanceof JCAssign && ((JCAssign) expr).lhs instanceof JCIdent) { - JCIdent id = (JCIdent) ((JCAssign) expr).lhs; - if ("value".equals(id.name.toString())) { - expr = ((JCAssign) expr).rhs; - } else { - annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); - continue outer; - } + if (allowRaw) { + result.append((JCAnnotation) valueOfParam); + } else { + addError(errorName, annotationNode); + continue outer; } - - if (expr instanceof JCAnnotation) { - result.append((JCAnnotation) expr); - } else if (expr instanceof JCNewArray) { - for (JCExpression expr2 : ((JCNewArray) expr).elems) { - if (expr2 instanceof JCAnnotation) { - result.append((JCAnnotation) expr2); + } else { + for (JCExpression expr : ((JCAnnotation) valueOfParam).args) { + if (expr instanceof JCAssign && ((JCAssign) expr).lhs instanceof JCIdent) { + JCIdent id = (JCIdent) ((JCAssign) expr).lhs; + if ("value".equals(id.name.toString())) { + expr = ((JCAssign) expr).rhs; } else { - annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); - continue outer; + addError(errorName, annotationNode); } } - } else { - annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); - continue outer; + + if (expr instanceof JCAnnotation) { + result.append((JCAnnotation) expr); + } else if (expr instanceof JCNewArray) { + for (JCExpression expr2 : ((JCNewArray) expr).elems) { + if (expr2 instanceof JCAnnotation) { + result.append((JCAnnotation) expr2); + } else { + addError(errorName, annotationNode); + continue outer; + } + } + } else { + addError(errorName, annotationNode); + continue outer; + } } } - } else { - if (valueOfParam instanceof JCNewArray && ((JCNewArray) valueOfParam).elems.isEmpty()) { - // Then we just remove it and move on (it's onMethod={} for example). + } else if (valueOfParam instanceof JCNewArray) { + JCNewArray arr = (JCNewArray) valueOfParam; + if (arr.elems.isEmpty()) { + // Just remove it, this is always fine. + } else if (allowRaw) { + for (JCExpression jce : arr.elems) { + if (jce instanceof JCAnnotation) result.append((JCAnnotation) jce); + else addError(errorName, annotationNode); + } } else { - annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + addError(errorName, annotationNode); } + } else { + addError(errorName, annotationNode); } } ast.args = params.toList(); return result.toList(); } + private static void addError(String errorName, JavacNode node) { + if (node.getLatestJavaSpecSupported() < 8) { + node.addError("The correct format up to JDK7 is " + errorName + "=@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + } else { + node.addError("The correct format for JDK8+ is " + errorName + "_={@SomeAnnotation, @SomeOtherAnnotation})"); + } + } + public static List<JCTypeParameter> copyTypeParams(JavacNode source, List<JCTypeParameter> params) { if (params == null || params.isEmpty()) return params; ListBuffer<JCTypeParameter> out = new ListBuffer<JCTypeParameter>(); diff --git a/test/transform/resource/after-delombok/OnXJava7Style.java b/test/transform/resource/after-delombok/OnXJava7Style.java new file mode 100644 index 00000000..f3a58d79 --- /dev/null +++ b/test/transform/resource/after-delombok/OnXJava7Style.java @@ -0,0 +1,43 @@ +//version :7 +public class OnXJava7Style { + @interface Foo { + String value() default ""; + } + @interface Bar { + String stuff() default ""; + } + String a; + String b; + String c; + String d; + String e; + @Foo + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public String getA() { + return this.a; + } + @Foo + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public void setB(final String b) { + this.b = b; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public void setC(@Foo("a") final String c) { + this.c = c; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public void setD(@Bar(stuff = "b") final String d) { + this.d = d; + } + @Foo("c") + @Bar(stuff = "d") + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public String getE() { + return this.e; + } +} diff --git a/test/transform/resource/after-delombok/OnXJava8Style.java b/test/transform/resource/after-delombok/OnXJava8Style.java new file mode 100644 index 00000000..b0ea96d6 --- /dev/null +++ b/test/transform/resource/after-delombok/OnXJava8Style.java @@ -0,0 +1,43 @@ +//version 8: +public class OnXJava8Style { + @interface Foo { + String value() default ""; + } + @interface Bar { + String stuff() default ""; + } + String a; + String b; + String c; + String d; + String e; + @Foo + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public String getA() { + return this.a; + } + @Foo + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public void setB(final String b) { + this.b = b; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public void setC(@Foo("a") final String c) { + this.c = c; + } + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public void setD(@Bar(stuff = "b") final String d) { + this.d = d; + } + @Foo("c") + @Bar(stuff = "d") + @java.lang.SuppressWarnings("all") + @javax.annotation.Generated("lombok") + public String getE() { + return this.e; + } +} diff --git a/test/transform/resource/after-ecj/OnXJava7Style.java b/test/transform/resource/after-ecj/OnXJava7Style.java new file mode 100644 index 00000000..415234d8 --- /dev/null +++ b/test/transform/resource/after-ecj/OnXJava7Style.java @@ -0,0 +1,31 @@ +public class OnXJava7Style { + @interface Foo { + String value() default ""; + } + @interface Bar { + String stuff() default ""; + } + @lombok.Getter() String a; + @lombok.Setter() String b; + @lombok.Setter() String c; + @lombok.Setter() String d; + @lombok.Getter() String e; + public OnXJava7Style() { + super(); + } + public @Foo @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getA() { + return this.a; + } + public @Foo() @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setB(final String b) { + this.b = b; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setC(final @Foo("a") String c) { + this.c = c; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setD(final @Bar(stuff = "b") String d) { + this.d = d; + } + public @Foo(value = "c") @Bar(stuff = "d") @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getE() { + return this.e; + } +} diff --git a/test/transform/resource/after-ecj/OnXJava7StyleOn8.java b/test/transform/resource/after-ecj/OnXJava7StyleOn8.java new file mode 100644 index 00000000..da51a5f0 --- /dev/null +++ b/test/transform/resource/after-ecj/OnXJava7StyleOn8.java @@ -0,0 +1,31 @@ +public class OnXJava7StyleOn8 { + @interface Foo { + String value() default ""; + } + @interface Bar { + String stuff() default ""; + } + @lombok.Getter() String a; + @lombok.Setter() String b; + @lombok.Setter() String c; + @lombok.Setter() String d; + @lombok.Getter() String e; + public OnXJava7StyleOn8() { + super(); + } + public @Foo @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getA() { + return this.a; + } + public @Foo() @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setB(final String b) { + this.b = b; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setC(final @Foo("a") String c) { + this.c = c; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setD(final @Bar(stuff = "b") String d) { + this.d = d; + } + public @Foo(value = "c") @Bar(stuff = "d") @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getE() { + return this.e; + } +} diff --git a/test/transform/resource/after-ecj/OnXJava8Style.java b/test/transform/resource/after-ecj/OnXJava8Style.java new file mode 100644 index 00000000..0e95a20b --- /dev/null +++ b/test/transform/resource/after-ecj/OnXJava8Style.java @@ -0,0 +1,31 @@ +public class OnXJava8Style { + @interface Foo { + String value() default ""; + } + @interface Bar { + String stuff() default ""; + } + @lombok.Getter() String a; + @lombok.Setter() String b; + @lombok.Setter() String c; + @lombok.Setter() String d; + @lombok.Getter() String e; + public OnXJava8Style() { + super(); + } + public @Foo @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getA() { + return this.a; + } + public @Foo() @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setB(final String b) { + this.b = b; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setC(final @Foo("a") String c) { + this.c = c; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setD(final @Bar(stuff = "b") String d) { + this.d = d; + } + public @Foo(value = "c") @Bar(stuff = "d") @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getE() { + return this.e; + } +} diff --git a/test/transform/resource/after-ecj/OnXJava8StyleOn7.java b/test/transform/resource/after-ecj/OnXJava8StyleOn7.java new file mode 100644 index 00000000..28afb5a7 --- /dev/null +++ b/test/transform/resource/after-ecj/OnXJava8StyleOn7.java @@ -0,0 +1,31 @@ +public class OnXJava8StyleOn7 { + @interface Foo { + String value() default ""; + } + @interface Bar { + String stuff() default ""; + } + @lombok.Getter() String a; + @lombok.Setter() String b; + @lombok.Setter() String c; + @lombok.Setter() String d; + @lombok.Getter() String e; + public OnXJava8StyleOn7() { + super(); + } + public @Foo @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getA() { + return this.a; + } + public @Foo() @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setB(final String b) { + this.b = b; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setC(final @Foo("a") String c) { + this.c = c; + } + public @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") void setD(final @Bar(stuff = "b") String d) { + this.d = d; + } + public @Foo(value = "c") @Bar(stuff = "d") @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") String getE() { + return this.e; + } +} diff --git a/test/transform/resource/before/GetterOnMethodErrors2.java b/test/transform/resource/before/GetterOnMethodErrors2.java index 2fd98c83..08ef13c6 100644 --- a/test/transform/resource/before/GetterOnMethodErrors2.java +++ b/test/transform/resource/before/GetterOnMethodErrors2.java @@ -1,3 +1,4 @@ +//version 8: class GetterOnMethodErrors2 { @lombok.Getter(onMethod=@_A_(@Deprecated)) private int bad1; @lombok.Getter(onMethod=@__(5)) private int bad2; diff --git a/test/transform/resource/before/OnXJava7Style.java b/test/transform/resource/before/OnXJava7Style.java new file mode 100644 index 00000000..6a3c35ff --- /dev/null +++ b/test/transform/resource/before/OnXJava7Style.java @@ -0,0 +1,16 @@ +//version :7 +public class OnXJava7Style { + @interface Foo { + String value() default ""; + } + + @interface Bar { + String stuff() default ""; + } + + @lombok.Getter(onMethod=@__(@Foo)) String a; + @lombok.Setter(onMethod=@__(@Foo())) String b; + @lombok.Setter(onParam=@__(@Foo("a"))) String c; + @lombok.Setter(onParam=@__(@Bar(stuff="b"))) String d; + @lombok.Getter(onMethod=@__({@Foo(value="c"), @Bar(stuff="d")})) String e; +} diff --git a/test/transform/resource/before/OnXJava7StyleOn8.java b/test/transform/resource/before/OnXJava7StyleOn8.java new file mode 100644 index 00000000..582fe6ce --- /dev/null +++ b/test/transform/resource/before/OnXJava7StyleOn8.java @@ -0,0 +1,18 @@ +//platform ecj +//version 8: + +public class OnXJava7StyleOn8 { + @interface Foo { + String value() default ""; + } + + @interface Bar { + String stuff() default ""; + } + + @lombok.Getter(onMethod=@__(@Foo)) String a; + @lombok.Setter(onMethod=@__(@Foo())) String b; + @lombok.Setter(onParam=@__(@Foo("a"))) String c; + @lombok.Setter(onParam=@__(@Bar(stuff="b"))) String d; + @lombok.Getter(onMethod=@__({@Foo(value="c"), @Bar(stuff="d")})) String e; +} diff --git a/test/transform/resource/before/OnXJava8Style.java b/test/transform/resource/before/OnXJava8Style.java new file mode 100644 index 00000000..ffb91727 --- /dev/null +++ b/test/transform/resource/before/OnXJava8Style.java @@ -0,0 +1,16 @@ +//version 8: +public class OnXJava8Style { + @interface Foo { + String value() default ""; + } + + @interface Bar { + String stuff() default ""; + } + + @lombok.Getter(onMethod_=@Foo) String a; + @lombok.Setter(onMethod_=@Foo()) String b; + @lombok.Setter(onParam_=@Foo("a")) String c; + @lombok.Setter(onParam_=@Bar(stuff="b")) String d; + @lombok.Getter(onMethod_={@Foo(value="c"), @Bar(stuff="d")}) String e; +} diff --git a/test/transform/resource/before/OnXJava8StyleOn7.java b/test/transform/resource/before/OnXJava8StyleOn7.java new file mode 100644 index 00000000..c006e468 --- /dev/null +++ b/test/transform/resource/before/OnXJava8StyleOn7.java @@ -0,0 +1,18 @@ +//platform ecj +//version :7 + +public class OnXJava8StyleOn7 { + @interface Foo { + String value() default ""; + } + + @interface Bar { + String stuff() default ""; + } + + @lombok.Getter(onMethod_=@Foo) String a; + @lombok.Setter(onMethod_=@Foo()) String b; + @lombok.Setter(onParam_=@Foo("a")) String c; + @lombok.Setter(onParam_=@Bar(stuff="b")) String d; + @lombok.Getter(onMethod_={@Foo(value="c"), @Bar(stuff="d")}) String e; +} diff --git a/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages b/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages index 53a9b9ad..6a181265 100644 --- a/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages +++ b/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages @@ -1,4 +1,4 @@ -2 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) -3 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) -4 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) -5 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +2 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) +3 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) +4 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) +5 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) diff --git a/test/transform/resource/messages-ecj/GetterOnMethodErrors2.java.messages b/test/transform/resource/messages-ecj/GetterOnMethodErrors2.java.messages index 53a9b9ad..6a181265 100644 --- a/test/transform/resource/messages-ecj/GetterOnMethodErrors2.java.messages +++ b/test/transform/resource/messages-ecj/GetterOnMethodErrors2.java.messages @@ -1,4 +1,4 @@ -2 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) -3 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) -4 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) -5 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +2 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) +3 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) +4 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) +5 The correct format is @Getter(onMethod_={@SomeAnnotation, @SomeOtherAnnotation}) diff --git a/usage_examples/experimental/onXExample_pre.jpage b/usage_examples/experimental/onXExample_pre.jpage index f8fcb435..e54371ae 100644 --- a/usage_examples/experimental/onXExample_pre.jpage +++ b/usage_examples/experimental/onXExample_pre.jpage @@ -9,7 +9,9 @@ import javax.validation.constraints.Max; @AllArgsConstructor(onConstructor=@__(@Inject)) public class OnXExample { - @Getter(onMethod=@__({@Id, @Column(name="unique-id")})) - @Setter(onParam=@__(@Max(10000))) +// @Getter(onMethod=@__({@Id, @Column(name="unique-id")})) //JDK7 +// @Setter(onParam=@__(@Max(10000))) //JDK7 + @Getter(onMethod_={@Id, @Column(name="unique-id")}) //JDK8 + @Setter(onParam_=@Max(10000)) //JDK8 private long unid; } diff --git a/website/features/experimental/onX.html b/website/features/experimental/onX.html index d4260dd5..39faa1bf 100644 --- a/website/features/experimental/onX.html +++ b/website/features/experimental/onX.html @@ -42,7 +42,9 @@ </p><p> <code>@Setter</code> and <code>@Wither</code> support <code>onParam</code> in addition to <code>onMethod</code>; annotations listed will be put on the only parameter that the generated method has. <code>@EqualsAndHashCode</code> also supports <code>onParam</code>; the listed annotation(s) will be placed on the single parameter of the generated <code>equals</code> method, as well as any generated <code>canEqual</code> method. </p><p> - The syntax is a little strange; to use any of the 3 <code>onX</code> features, you must wrap the annotations to be applied to the constructor / method / parameter in <code>@__(@AnnotationGoesHere)</code>. To apply multiple annotations, use <code>@__({@Annotation1, @Annotation2})</code>. The annotations can themselves obviously have parameters as well. + The syntax is a little strange and depends on the javac you are using.<br /> + On javac7, to use any of the 3 <code>onX</code> features, you must wrap the annotations to be applied to the constructor / method / parameter in <code>@__(@AnnotationGoesHere)</code>. To apply multiple annotations, use <code>@__({@Annotation1, @Annotation2})</code>. The annotations can themselves obviously have parameters as well.<br /> + On javac8 and up, you add an underscore after <code>onMethod</code>, <code>onParam</code>, or <code>onConstructor</code>. </p> </div> <div class="snippets"> @@ -62,6 +64,8 @@ <p> The reason of the weird syntax is to make this feature work in javac 7 compilers; the <code>@__</code> type is an annotation reference to the annotation type <code>__</code> (double underscore) which doesn't actually exist; this makes javac 7 delay aborting the compilation process due to an error because it is possible an annotation processor will later create the <code>__</code> type. Instead, lombok applies the annotations and removes the references so that the error will never actually occur. The point is: The <code>__</code> type <em>must not exist</em>, otherwise the feature does not work. In the rare case that the <code>__</code> type does exist (and is imported or in the package), you can simply add more underscores. Technically any non-existent type would work, but to maintain consistency and readability and catch erroneous use, lombok considers it an error if the 'wrapper' annotation is anything but a series of underscores. </p><p> + In javac8, the above feature should work but due to a bug in javac8 it does not. However, starting in javac8, if the parameter name does not exist in the annotation type, compilation proceeds to a phase where lombok can fix it. + </p><p> To reiterate: This feature can disappear at any time; if you use this feature, be prepared to adjust your code when we find a nicer way of implementing this feature, or, if a future version of javac forces us to remove this feature entirely with no alternative. </p><p> The <code>onX</code> parameter is not legal on any type-wide variant. For example, a <code>@Getter</code> annotation on a class does not support <code>onMethod</code>. |