From 7faff42d85c2e21b9a3b2e0014ebc36dbe5eb155 Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Sun, 11 Aug 2013 20:39:30 +0200 Subject: Fixed two typos --- src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/lombok/eclipse/handlers') diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 6990e609..3c8a7039 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -221,7 +221,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler Date: Mon, 12 Aug 2013 22:51:13 +0200 Subject: replaced double underscore as new standard way of writing the dummy annotation for onX instead of single underscore, which emits warnings on javac8+. Also made dollars and Xes legal in addition to underscores, in case double underscore disappears later too. --- .../eclipse/handlers/EclipseHandlerUtil.java | 36 +++++++++++----------- .../lombok/javac/handlers/JavacHandlerUtil.java | 12 ++++---- test/transform/resource/before/Constructors.java | 2 +- test/transform/resource/before/GetterOnMethod.java | 4 +-- .../resource/before/GetterOnMethodErrors.java | 2 +- .../resource/before/GetterOnMethodErrors2.java | 14 ++++----- .../resource/before/SetterOnMethodOnParam.java | 4 +-- .../GetterOnMethodErrors2.java.messages | 8 ++--- .../GetterOnMethodErrors2.java.messages | 8 ++--- 9 files changed, 45 insertions(+), 45 deletions(-) (limited to 'src/core/lombok/eclipse/handlers') diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 9bd634f7..d74b8981 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1562,9 +1562,9 @@ public class EclipseHandlerUtil { intLiteralFactoryMethod = intLiteralFactoryMethod_; } - private static boolean isAllUnderscores(char[] in) { + private static boolean isAllValidOnXCharacters(char[] in) { if (in == null || in.length == 0) return false; - for (char c : in) if (c != '_') return false; + for (char c : in) if (c != '_' && c != 'X' && c != 'x' && c != '$') return false; return true; } @@ -1597,31 +1597,31 @@ public class EclipseHandlerUtil { 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 ...})', + // 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}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); return Collections.emptyList(); } - Annotation atUnderscore = (Annotation) value; - if (!(atUnderscore.type instanceof SingleTypeReference) || - !isAllUnderscores(((SingleTypeReference) atUnderscore.type).token)) { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + 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 (atUnderscore instanceof MarkerAnnotation) { - // It's @getter(onMethod=@_). This is weird, but fine. + if (atDummyIdentifier instanceof MarkerAnnotation) { + // It's @Getter(onMethod=@__). This is weird, but fine. return Collections.emptyList(); } Expression content = null; - if (atUnderscore instanceof NormalAnnotation) { - MemberValuePair[] mvps = ((NormalAnnotation) atUnderscore).memberValuePairs; + if (atDummyIdentifier instanceof NormalAnnotation) { + MemberValuePair[] mvps = ((NormalAnnotation) atDummyIdentifier).memberValuePairs; if (mvps == null || mvps.length == 0) { - // It's @getter(onMethod=@_()). This is weird, but fine. + // It's @Getter(onMethod=@__()). This is weird, but fine. return Collections.emptyList(); } if (mvps.length == 1 && Arrays.equals("value".toCharArray(), mvps[0].name)) { @@ -1629,12 +1629,12 @@ public class EclipseHandlerUtil { } } - if (atUnderscore instanceof SingleMemberAnnotation) { - content = ((SingleMemberAnnotation) atUnderscore).memberValue; + if (atDummyIdentifier instanceof SingleMemberAnnotation) { + content = ((SingleMemberAnnotation) atDummyIdentifier).memberValue; } if (content == null) { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); return Collections.emptyList(); } @@ -1646,13 +1646,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}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); return Collections.emptyList(); } } return result; } else { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); return Collections.emptyList(); } } diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 65997f9a..d518c4df 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1009,9 +1009,9 @@ public class JavacHandlerUtil { if (valueOfParam instanceof JCAnnotation) { String dummyAnnotationName = ((JCAnnotation) valueOfParam).annotationType.toString(); - dummyAnnotationName = dummyAnnotationName.replace("_", ""); + dummyAnnotationName = dummyAnnotationName.replace("_", "").replace("$", "").replace("x", "").replace("X", ""); if (dummyAnnotationName.length() > 0) { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } for (JCExpression expr : ((JCAnnotation) valueOfParam).args) { @@ -1020,7 +1020,7 @@ public class JavacHandlerUtil { if ("value".equals(id.name.toString())) { expr = ((JCAssign) expr).rhs; } else { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } } @@ -1032,12 +1032,12 @@ public class JavacHandlerUtil { if (expr2 instanceof JCAnnotation) { result.append((JCAnnotation) expr2); } else { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } } } else { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } } @@ -1045,7 +1045,7 @@ public class JavacHandlerUtil { if (valueOfParam instanceof JCNewArray && ((JCNewArray) valueOfParam).elems.isEmpty()) { // Then we just remove it and move on (it's onMethod={} for example). } else { - errorNode.addError("The correct format is " + errorName + "@_({@SomeAnnotation, @SomeOtherAnnotation}))"); + errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); } } } diff --git a/test/transform/resource/before/Constructors.java b/test/transform/resource/before/Constructors.java index 0ab7b7f0..45b3a199 100644 --- a/test/transform/resource/before/Constructors.java +++ b/test/transform/resource/before/Constructors.java @@ -10,7 +10,7 @@ final int x; String name; } -@lombok.RequiredArgsConstructor(onConstructor=@_(@Deprecated)) class RequiredArgsConstructorWithAnnotations { +@lombok.RequiredArgsConstructor(onConstructor=@__(@Deprecated)) class RequiredArgsConstructorWithAnnotations { final int x; String name; } diff --git a/test/transform/resource/before/GetterOnMethod.java b/test/transform/resource/before/GetterOnMethod.java index 558f3f64..3e56a66e 100644 --- a/test/transform/resource/before/GetterOnMethod.java +++ b/test/transform/resource/before/GetterOnMethod.java @@ -1,6 +1,6 @@ class GetterOnMethod { - @lombok.Getter(onMethod=@_(@Deprecated)) int i; - @lombok.Getter(onMethod=@_({@java.lang.Deprecated, @Test})) int j, k; + @lombok.Getter(onMethod=@__(@Deprecated)) int i; + @lombok.Getter(onMethod=@__({@java.lang.Deprecated, @Test})) int j, k; public @interface Test { } diff --git a/test/transform/resource/before/GetterOnMethodErrors.java b/test/transform/resource/before/GetterOnMethodErrors.java index ae5d5b54..ec4704f0 100644 --- a/test/transform/resource/before/GetterOnMethodErrors.java +++ b/test/transform/resource/before/GetterOnMethodErrors.java @@ -1,6 +1,6 @@ class PlaceFillerToMakeSurePositionIsRelevant { } -@lombok.Getter(onMethod=@_(@Deprecated)) +@lombok.Getter(onMethod=@__(@Deprecated)) class GetterOnMethodErrors { private int test; } diff --git a/test/transform/resource/before/GetterOnMethodErrors2.java b/test/transform/resource/before/GetterOnMethodErrors2.java index 423183b9..2fd98c83 100644 --- a/test/transform/resource/before/GetterOnMethodErrors2.java +++ b/test/transform/resource/before/GetterOnMethodErrors2.java @@ -1,12 +1,12 @@ class GetterOnMethodErrors2 { @lombok.Getter(onMethod=@_A_(@Deprecated)) private int bad1; - @lombok.Getter(onMethod=@_(5)) private int bad2; - @lombok.Getter(onMethod=@_({@Deprecated, 5})) private int bad3; - @lombok.Getter(onMethod=@_(bar=@Deprecated)) private int bad4; - @lombok.Getter(onMethod=@_) private int good1; - @lombok.Getter(onMethod=@_()) private int good2; - @lombok.Getter(onMethod=@_(value=@Deprecated)) private int good3; - @lombok.Getter(onMethod=@_(value={@Deprecated, @Test})) private int good4; + @lombok.Getter(onMethod=@__(5)) private int bad2; + @lombok.Getter(onMethod=@__({@Deprecated, 5})) private int bad3; + @lombok.Getter(onMethod=@$(bar=@Deprecated)) private int bad4; + @lombok.Getter(onMethod=@__) private int good1; + @lombok.Getter(onMethod=@X()) private int good2; + @lombok.Getter(onMethod=@__(value=@Deprecated)) private int good3; + @lombok.Getter(onMethod=@xXx$$(value={@Deprecated, @Test})) private int good4; public @interface Test { } } diff --git a/test/transform/resource/before/SetterOnMethodOnParam.java b/test/transform/resource/before/SetterOnMethodOnParam.java index 70f3dc8c..4e042cf4 100644 --- a/test/transform/resource/before/SetterOnMethodOnParam.java +++ b/test/transform/resource/before/SetterOnMethodOnParam.java @@ -1,6 +1,6 @@ class SetterOnMethodOnParam { - @lombok.Setter(onMethod=@_(@Deprecated)) int i; - @lombok.Setter(onMethod=@_({@java.lang.Deprecated, @Test}), onParam=@_(@Test)) int j, k; + @lombok.Setter(onMethod=@__(@Deprecated)) int i; + @lombok.Setter(onMethod=@__({@java.lang.Deprecated, @Test}), onParam=@__(@Test)) int j, k; public @interface Test { } diff --git a/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages b/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages index ead040a5..b5bad45b 100644 --- a/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages +++ b/test/transform/resource/messages-delombok/GetterOnMethodErrors2.java.messages @@ -1,4 +1,4 @@ -2:9 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) -3:9 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) -4:9 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) -5:9 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) +2:9 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +3:9 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +4:9 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +5:9 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 6b949f41..f87b9f13 100644 --- a/test/transform/resource/messages-ecj/GetterOnMethodErrors2.java.messages +++ b/test/transform/resource/messages-ecj/GetterOnMethodErrors2.java.messages @@ -1,4 +1,4 @@ -2:31 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) -3:93 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) -4:143 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) -5:208 The correct format is @Getter(onMethod=@_({@SomeAnnotation, @SomeOtherAnnotation})) +2:31 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +3:93 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +4:144 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) +5:210 The correct format is @Getter(onMethod=@__({@SomeAnnotation, @SomeOtherAnnotation})) -- cgit From b93318da303b36957f3774015090121af778665f Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 14 Aug 2013 07:05:31 +0200 Subject: * Fixed 553: @XArgsConstructor (and @Builder on a class) did not look at @Accessors to handle field accessors. * various operations on names in javac were really slow; they are faster now. --- src/core/lombok/core/TransformationsUtil.java | 5 +- .../eclipse/handlers/EclipseHandlerUtil.java | 31 +++++++++++ .../lombok/eclipse/handlers/HandleBuilder.java | 2 +- .../lombok/eclipse/handlers/HandleConstructor.java | 11 ++-- .../lombok/javac/handlers/HandleAccessors.java | 2 + src/core/lombok/javac/handlers/HandleBuilder.java | 2 +- .../lombok/javac/handlers/HandleConstructor.java | 17 ++++-- .../lombok/javac/handlers/JavacHandlerUtil.java | 31 +++++++++++ .../after-delombok/BuilderWithAccessors.java | 65 ++++++++++++++++++++++ .../after-delombok/ConstructorsWithAccessors.java | 15 +++++ .../resource/after-ecj/BuilderWithAccessors.java | 47 ++++++++++++++++ .../after-ecj/ConstructorsWithAccessors.java | 13 +++++ .../resource/before/BuilderWithAccessors.java | 7 +++ .../resource/before/ConstructorsWithAccessors.java | 6 ++ 14 files changed, 241 insertions(+), 13 deletions(-) create mode 100644 test/transform/resource/after-delombok/BuilderWithAccessors.java create mode 100644 test/transform/resource/after-delombok/ConstructorsWithAccessors.java create mode 100644 test/transform/resource/after-ecj/BuilderWithAccessors.java create mode 100644 test/transform/resource/after-ecj/ConstructorsWithAccessors.java create mode 100644 test/transform/resource/before/BuilderWithAccessors.java create mode 100644 test/transform/resource/before/ConstructorsWithAccessors.java (limited to 'src/core/lombok/eclipse/handlers') diff --git a/src/core/lombok/core/TransformationsUtil.java b/src/core/lombok/core/TransformationsUtil.java index 8959ad7a..8c7fbd3f 100644 --- a/src/core/lombok/core/TransformationsUtil.java +++ b/src/core/lombok/core/TransformationsUtil.java @@ -70,9 +70,11 @@ public class TransformationsUtil { * @param prefixes A list of prefixes, usually provided by the {@code Accessors} settings annotation, listing field prefixes. * @return The base name of the field. */ - private static CharSequence removePrefix(CharSequence fieldName, String[] prefixes) { + public static CharSequence removePrefix(CharSequence fieldName, String[] prefixes) { if (prefixes == null || prefixes.length == 0) return fieldName; + fieldName = fieldName.toString(); + outer: for (String prefix : prefixes) { if (prefix.length() == 0) return fieldName; @@ -176,6 +178,7 @@ public class TransformationsUtil { private static String toAccessorName(AnnotationValues accessors, CharSequence fieldName, boolean isBoolean, String booleanPrefix, String normalPrefix, boolean adhereToFluent) { + fieldName = fieldName.toString(); if (fieldName.length() == 0) return null; Accessors ac = accessors == null ? null : accessors.getInstance(); diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 9bd634f7..d666084e 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1125,6 +1125,37 @@ public class EclipseHandlerUtil { return true; } + public static char[] removePrefixFromField(EclipseNode field) { + String[] prefixes = null; + for (EclipseNode node : field.down()) { + if (annotationTypeMatches(Accessors.class, node)) { + prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + break; + } + } + + if (prefixes == null) { + EclipseNode current = field.up(); + outer: + while (current != null) { + for (EclipseNode node : current.down()) { + if (annotationTypeMatches(Accessors.class, node)) { + prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + break outer; + } + } + current = current.up(); + } + } + + if (prefixes != null && prefixes.length > 0) { + CharSequence newName = TransformationsUtil.removePrefix(field.getName(), prefixes); + if (newName != null) return newName.toString().toCharArray(); + } + + return ((FieldDeclaration) field.get()).name; + } + public static AnnotationValues getAccessorsForField(EclipseNode field) { for (EclipseNode node : field.down()) { if (annotationTypeMatches(Accessors.class, node)) { diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 70110a9c..981d77dc 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -113,7 +113,7 @@ public class HandleBuilder extends EclipseAnnotationHandler { // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves. // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; - namesOfParameters.add(fd.name); + namesOfParameters.add(removePrefixFromField(fieldNode)); typesOfParameters.add(fd.type); fields.add(fieldNode); } diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index 1ae680d9..22285b2d 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -230,7 +230,8 @@ public class HandleConstructor { int ctr = 0; for (EclipseNode field : fields) { - fieldNames.expressions[ctr] = new StringLiteral(field.getName().toCharArray(), pS, pE, 0); + char[] fieldName = removePrefixFromField(field); + fieldNames.expressions[ctr] = new StringLiteral(fieldName, pS, pE, 0); setGeneratedBy(fieldNames.expressions[ctr], source); ctr++; } @@ -273,15 +274,17 @@ public class HandleConstructor { for (EclipseNode fieldNode : fields) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); - FieldReference thisX = new FieldReference(field.name, p); + char[] rawName = field.name; + char[] fieldName = removePrefixFromField(fieldNode); + FieldReference thisX = new FieldReference(rawName, p); thisX.receiver = new ThisReference((int)(p >> 32), (int)p); - SingleNameReference assignmentNameRef = new SingleNameReference(field.name, 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); assigns.add(assignment); long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd; - Argument parameter = new Argument(field.name, fieldPos, copyType(field.type, source), Modifier.FINAL); + Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL); Annotation[] nonNulls = findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN); Annotation[] nullables = findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN); if (nonNulls.length != 0) { diff --git a/src/core/lombok/javac/handlers/HandleAccessors.java b/src/core/lombok/javac/handlers/HandleAccessors.java index 7aeec6bb..e2489bda 100644 --- a/src/core/lombok/javac/handlers/HandleAccessors.java +++ b/src/core/lombok/javac/handlers/HandleAccessors.java @@ -28,11 +28,13 @@ import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; import lombok.experimental.Accessors; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @ProviderFor(JavacAnnotationHandler.class) +@HandlerPriority(65536) public class HandleAccessors extends JavacAnnotationHandler { @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { // Accessors itself is handled by HandleGetter/Setter; this is just to ensure that the annotation is removed diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 6422f5ed..7e5662cd 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -106,7 +106,7 @@ public class HandleBuilder extends JavacAnnotationHandler { // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves. // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; - namesOfParameters.add(fd.name); + namesOfParameters.add(removePrefixFromField(fieldNode)); typesOfParameters.add(fd.vartype); allFields.append(fieldNode); } diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index ecd982e9..df8739de 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -52,6 +52,7 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) @@ -210,7 +211,8 @@ public class HandleConstructor { JCExpression constructorPropertiesType = chainDots(node, "java", "beans", "ConstructorProperties"); ListBuffer fieldNames = ListBuffer.lb(); for (JavacNode field : fields) { - fieldNames.append(maker.Literal(field.getName())); + Name fieldName = removePrefixFromField(field); + fieldNames.append(maker.Literal(fieldName.toString())); } JCExpression fieldNamesArray = maker.NewArray(null, List.nil(), fieldNames.toList()); JCAnnotation annotation = maker.Annotation(constructorPropertiesType, List.of(fieldNamesArray)); @@ -229,12 +231,14 @@ public class HandleConstructor { for (JavacNode fieldNode : fields) { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); + Name fieldName = removePrefixFromField(fieldNode); + Name rawName = field.name; List nonNulls = findAnnotations(fieldNode, TransformationsUtil.NON_NULL_PATTERN); List nullables = findAnnotations(fieldNode, TransformationsUtil.NULLABLE_PATTERN); - JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.FINAL, nonNulls.appendList(nullables)), field.name, field.vartype, null); + JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.FINAL, nonNulls.appendList(nullables)), fieldName, field.vartype, null); params.append(param); - JCFieldAccess thisX = maker.Select(maker.Ident(fieldNode.toName("this")), field.name); - JCAssign assign = maker.Assign(thisX, maker.Ident(field.name)); + JCFieldAccess thisX = maker.Select(maker.Ident(fieldNode.toName("this")), rawName); + JCAssign assign = maker.Assign(thisX, maker.Ident(fieldName)); assigns.append(maker.Exec(assign)); if (!nonNulls.isEmpty()) { @@ -289,12 +293,13 @@ public class HandleConstructor { for (JavacNode fieldNode : fields) { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); + Name fieldName = removePrefixFromField(fieldNode); JCExpression pType = cloneType(maker, field.vartype, source); List nonNulls = findAnnotations(fieldNode, TransformationsUtil.NON_NULL_PATTERN); List nullables = findAnnotations(fieldNode, TransformationsUtil.NULLABLE_PATTERN); - JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.FINAL, nonNulls.appendList(nullables)), field.name, pType, null); + JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.FINAL, nonNulls.appendList(nullables)), fieldName, pType, null); params.append(param); - args.append(maker.Ident(field.name)); + args.append(maker.Ident(fieldName)); } JCReturn returnStatement = maker.Return(maker.NewClass(null, List.nil(), constructorType, args.toList(), null)); JCBlock body = maker.Block(0, List.of(returnStatement)); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index d7d29da2..ae3fddbf 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -456,6 +456,37 @@ public class JavacHandlerUtil { return varType != null && varType.toString().equals("boolean"); } + public static Name removePrefixFromField(JavacNode field) { + String[] prefixes = null; + for (JavacNode node : field.down()) { + if (annotationTypeMatches(Accessors.class, node)) { + prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + break; + } + } + + if (prefixes == null) { + JavacNode current = field.up(); + outer: + while (current != null) { + for (JavacNode node : current.down()) { + if (annotationTypeMatches(Accessors.class, node)) { + prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + break outer; + } + } + current = current.up(); + } + } + + if (prefixes != null && prefixes.length > 0) { + CharSequence newName = TransformationsUtil.removePrefix(field.getName(), prefixes); + if (newName != null) return field.toName(newName.toString()); + } + + return ((JCVariableDecl) field.get()).name; + } + public static AnnotationValues getAccessorsForField(JavacNode field) { for (JavacNode node : field.down()) { if (annotationTypeMatches(Accessors.class, node)) { diff --git a/test/transform/resource/after-delombok/BuilderWithAccessors.java b/test/transform/resource/after-delombok/BuilderWithAccessors.java new file mode 100644 index 00000000..503c33be --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderWithAccessors.java @@ -0,0 +1,65 @@ +class BuilderWithAccessors { + private final int plower; + private final int pUpper; + private int _foo; + private int __bar; + @java.lang.SuppressWarnings("all") + BuilderWithAccessors(final int plower, final int upper, final int foo, final int _bar) { + this.plower = plower; + this.pUpper = upper; + this._foo = foo; + this.__bar = _bar; + } + @java.lang.SuppressWarnings("all") + public static class BuilderWithAccessorsBuilder { + private int plower; + private int upper; + private int foo; + private int _bar; + + @java.lang.SuppressWarnings("all") + BuilderWithAccessorsBuilder() { + + } + + @java.lang.SuppressWarnings("all") + public BuilderWithAccessorsBuilder plower(final int plower) { + this.plower = plower; + return this; + } + + @java.lang.SuppressWarnings("all") + public BuilderWithAccessorsBuilder upper(final int upper) { + this.upper = upper; + return this; + } + + @java.lang.SuppressWarnings("all") + public BuilderWithAccessorsBuilder foo(final int foo) { + this.foo = foo; + return this; + } + + @java.lang.SuppressWarnings("all") + public BuilderWithAccessorsBuilder _bar(final int _bar) { + this._bar = _bar; + return this; + } + + @java.lang.SuppressWarnings("all") + public BuilderWithAccessors build() { + return new BuilderWithAccessors(plower, upper, foo, _bar); + } + + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "BuilderWithAccessors.BuilderWithAccessorsBuilder(plower=" + this.plower + ", upper=" + this.upper + ", foo=" + this.foo + ", _bar=" + this._bar + ")"; + } + } + + @java.lang.SuppressWarnings("all") + public static BuilderWithAccessorsBuilder builder() { + return new BuilderWithAccessorsBuilder(); + } +} \ No newline at end of file diff --git a/test/transform/resource/after-delombok/ConstructorsWithAccessors.java b/test/transform/resource/after-delombok/ConstructorsWithAccessors.java new file mode 100644 index 00000000..9937c7ee --- /dev/null +++ b/test/transform/resource/after-delombok/ConstructorsWithAccessors.java @@ -0,0 +1,15 @@ +class ConstructorsWithAccessors { + int plower; + int pUpper; + int _huh; + int __huh2; + + @java.beans.ConstructorProperties({"plower", "upper", "huh", "_huh2"}) + @java.lang.SuppressWarnings("all") + public ConstructorsWithAccessors(final int plower, final int upper, final int huh, final int _huh2) { + this.plower = plower; + this.pUpper = upper; + this._huh = huh; + this.__huh2 = _huh2; + } +} diff --git a/test/transform/resource/after-ecj/BuilderWithAccessors.java b/test/transform/resource/after-ecj/BuilderWithAccessors.java new file mode 100644 index 00000000..79776dc4 --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderWithAccessors.java @@ -0,0 +1,47 @@ +@lombok.experimental.Builder @lombok.experimental.Accessors(prefix = {"p", "_"}) class BuilderWithAccessors { + public static @java.lang.SuppressWarnings("all") class BuilderWithAccessorsBuilder { + private int plower; + private int upper; + private int foo; + private int _bar; + @java.lang.SuppressWarnings("all") BuilderWithAccessorsBuilder() { + super(); + } + public @java.lang.SuppressWarnings("all") BuilderWithAccessorsBuilder plower(final int plower) { + this.plower = plower; + return this; + } + public @java.lang.SuppressWarnings("all") BuilderWithAccessorsBuilder upper(final int upper) { + this.upper = upper; + return this; + } + public @java.lang.SuppressWarnings("all") BuilderWithAccessorsBuilder foo(final int foo) { + this.foo = foo; + return this; + } + public @java.lang.SuppressWarnings("all") BuilderWithAccessorsBuilder _bar(final int _bar) { + this._bar = _bar; + return this; + } + public @java.lang.SuppressWarnings("all") BuilderWithAccessors build() { + return new BuilderWithAccessors(plower, upper, foo, _bar); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (((((((("BuilderWithAccessors.BuilderWithAccessorsBuilder(plower=" + this.plower) + ", upper=") + this.upper) + ", foo=") + this.foo) + ", _bar=") + this._bar) + ")"); + } + } + private final int plower; + private final int pUpper; + private int _foo; + private int __bar; + @java.lang.SuppressWarnings("all") BuilderWithAccessors(final int plower, final int upper, final int foo, final int _bar) { + super(); + this.plower = plower; + this.pUpper = upper; + this._foo = foo; + this.__bar = _bar; + } + public static @java.lang.SuppressWarnings("all") BuilderWithAccessorsBuilder builder() { + return new BuilderWithAccessorsBuilder(); + } +} diff --git a/test/transform/resource/after-ecj/ConstructorsWithAccessors.java b/test/transform/resource/after-ecj/ConstructorsWithAccessors.java new file mode 100644 index 00000000..0df8fbe1 --- /dev/null +++ b/test/transform/resource/after-ecj/ConstructorsWithAccessors.java @@ -0,0 +1,13 @@ +@lombok.AllArgsConstructor @lombok.experimental.Accessors(prefix = {"p", "_"}) class ConstructorsWithAccessors { + int plower; + int pUpper; + int _huh; + int __huh2; + public @java.beans.ConstructorProperties({"plower", "upper", "huh", "_huh2"}) @java.lang.SuppressWarnings("all") ConstructorsWithAccessors(final int plower, final int upper, final int huh, final int _huh2) { + super(); + this.plower = plower; + this.pUpper = upper; + this._huh = huh; + this.__huh2 = _huh2; + } +} diff --git a/test/transform/resource/before/BuilderWithAccessors.java b/test/transform/resource/before/BuilderWithAccessors.java new file mode 100644 index 00000000..4f5660a1 --- /dev/null +++ b/test/transform/resource/before/BuilderWithAccessors.java @@ -0,0 +1,7 @@ +@lombok.experimental.Builder @lombok.experimental.Accessors(prefix={"p", "_"}) +class BuilderWithAccessors { + private final int plower; + private final int pUpper; + private int _foo; + private int __bar; +} diff --git a/test/transform/resource/before/ConstructorsWithAccessors.java b/test/transform/resource/before/ConstructorsWithAccessors.java new file mode 100644 index 00000000..d242317d --- /dev/null +++ b/test/transform/resource/before/ConstructorsWithAccessors.java @@ -0,0 +1,6 @@ +@lombok.AllArgsConstructor @lombok.experimental.Accessors(prefix={"p", "_"}) class ConstructorsWithAccessors { + int plower; + int pUpper; + int _huh; + int __huh2; +} -- cgit From 72b55dccb18f38b8aefd0ac8e7c2e8bd2dd5c057 Mon Sep 17 00:00:00 2001 From: Sander Koning Date: Fri, 20 Sep 2013 17:13:39 +0200 Subject: Issue 559: NullPointerException when @NonNull is used in abstract method - Prevent NPE in javac and give a proper warning in both eclipse and javac - Add test cases --- src/core/lombok/eclipse/handlers/NonNullHandler.java | 5 +++++ src/core/lombok/javac/handlers/NonNullHandler.java | 5 +++++ .../resource/after-delombok/NonNullOnParameterAbstract.java | 10 ++++++++++ .../resource/after-ecj/NonNullOnParameterAbstract.java | 13 +++++++++++++ .../resource/before/NonNullOnParameterAbstract.java | 7 +++++++ .../NonNullOnParameterAbstract.java.messages | 1 + .../messages-ecj/NonNullOnParameterAbstract.java.messages | 1 + .../NonNullOnParameterAbstract.java.messages | 1 + 8 files changed, 43 insertions(+) create mode 100644 test/transform/resource/after-delombok/NonNullOnParameterAbstract.java create mode 100644 test/transform/resource/after-ecj/NonNullOnParameterAbstract.java create mode 100644 test/transform/resource/before/NonNullOnParameterAbstract.java create mode 100644 test/transform/resource/messages-delombok/NonNullOnParameterAbstract.java.messages create mode 100644 test/transform/resource/messages-ecj/NonNullOnParameterAbstract.java.messages create mode 100644 test/transform/resource/messages-idempotent/NonNullOnParameterAbstract.java.messages (limited to 'src/core/lombok/eclipse/handlers') diff --git a/src/core/lombok/eclipse/handlers/NonNullHandler.java b/src/core/lombok/eclipse/handlers/NonNullHandler.java index 5c58069c..59fda801 100644 --- a/src/core/lombok/eclipse/handlers/NonNullHandler.java +++ b/src/core/lombok/eclipse/handlers/NonNullHandler.java @@ -82,6 +82,11 @@ public class NonNullHandler extends EclipseAnnotationHandler { if (isGenerated(declaration)) return; + if (declaration.isAbstract()) { + annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method."); + return; + } + // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and // wrap all references to it in the super/this to a call to this method. diff --git a/src/core/lombok/javac/handlers/NonNullHandler.java b/src/core/lombok/javac/handlers/NonNullHandler.java index 415d6032..392e8390 100644 --- a/src/core/lombok/javac/handlers/NonNullHandler.java +++ b/src/core/lombok/javac/handlers/NonNullHandler.java @@ -76,6 +76,11 @@ public class NonNullHandler extends JavacAnnotationHandler { if (JavacHandlerUtil.isGenerated(declaration)) return; + if (declaration.body == null) { + annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method."); + return; + } + // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and // wrap all references to it in the super/this to a call to this method. diff --git a/test/transform/resource/after-delombok/NonNullOnParameterAbstract.java b/test/transform/resource/after-delombok/NonNullOnParameterAbstract.java new file mode 100644 index 00000000..e0330bd6 --- /dev/null +++ b/test/transform/resource/after-delombok/NonNullOnParameterAbstract.java @@ -0,0 +1,10 @@ +abstract class NonNullOnParameterAbstract { + public void test(@lombok.NonNull String arg) { + if (arg == null) { + throw new java.lang.NullPointerException("arg"); + } + System.out.println("Hey"); + } + + public abstract void test2(@lombok.NonNull String arg); +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/NonNullOnParameterAbstract.java b/test/transform/resource/after-ecj/NonNullOnParameterAbstract.java new file mode 100644 index 00000000..a7dae247 --- /dev/null +++ b/test/transform/resource/after-ecj/NonNullOnParameterAbstract.java @@ -0,0 +1,13 @@ +abstract class NonNullOnParameterAbstract { + NonNullOnParameterAbstract() { + super(); + } + public void test(@lombok.NonNull String arg) { + if ((arg == null)) + { + throw new java.lang.NullPointerException("arg"); + } + System.out.println("Hey"); + } + public abstract void test2(@lombok.NonNull String arg); +} \ No newline at end of file diff --git a/test/transform/resource/before/NonNullOnParameterAbstract.java b/test/transform/resource/before/NonNullOnParameterAbstract.java new file mode 100644 index 00000000..16691184 --- /dev/null +++ b/test/transform/resource/before/NonNullOnParameterAbstract.java @@ -0,0 +1,7 @@ +abstract class NonNullOnParameterAbstract { + public void test(@lombok.NonNull String arg) { + System.out.println("Hey"); + } + + public abstract void test2(@lombok.NonNull String arg); +} \ No newline at end of file diff --git a/test/transform/resource/messages-delombok/NonNullOnParameterAbstract.java.messages b/test/transform/resource/messages-delombok/NonNullOnParameterAbstract.java.messages new file mode 100644 index 00000000..1ebeea05 --- /dev/null +++ b/test/transform/resource/messages-delombok/NonNullOnParameterAbstract.java.messages @@ -0,0 +1 @@ +6:36 @NonNull is meaningless on a parameter of an abstract method. diff --git a/test/transform/resource/messages-ecj/NonNullOnParameterAbstract.java.messages b/test/transform/resource/messages-ecj/NonNullOnParameterAbstract.java.messages new file mode 100644 index 00000000..48d1508e --- /dev/null +++ b/test/transform/resource/messages-ecj/NonNullOnParameterAbstract.java.messages @@ -0,0 +1 @@ +6:154 @NonNull is meaningless on a parameter of an abstract method. \ No newline at end of file diff --git a/test/transform/resource/messages-idempotent/NonNullOnParameterAbstract.java.messages b/test/transform/resource/messages-idempotent/NonNullOnParameterAbstract.java.messages new file mode 100644 index 00000000..00547171 --- /dev/null +++ b/test/transform/resource/messages-idempotent/NonNullOnParameterAbstract.java.messages @@ -0,0 +1 @@ +9:36 @NonNull is meaningless on a parameter of an abstract method. \ No newline at end of file -- cgit From a9b4fb0c685fbc52079d57532c04277e78c95ec2 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Thu, 10 Oct 2013 22:36:16 +0200 Subject: Fix for issues when mixing @NonNull on params with @SneakyThrows or @Synchronized [Issue #588] --- doc/changelog.markdown | 9 +- src/core/lombok/core/Version.java | 2 +- .../lombok/eclipse/handlers/HandleNonNull.java | 175 ++++++++++++++++++++ .../eclipse/handlers/HandleSneakyThrows.java | 2 + .../eclipse/handlers/HandleSynchronized.java | 2 + .../lombok/eclipse/handlers/NonNullHandler.java | 152 ----------------- src/core/lombok/javac/handlers/HandleNonNull.java | 179 +++++++++++++++++++++ .../lombok/javac/handlers/HandleSneakyThrows.java | 2 + .../lombok/javac/handlers/HandleSynchronized.java | 2 + src/core/lombok/javac/handlers/NonNullHandler.java | 156 ------------------ .../after-delombok/NonNullWithSneakyThrows.java | 12 ++ test/transform/resource/after-ecj/InjectField.java | 4 +- .../after-ecj/NonNullWithSneakyThrows.java | 18 +++ .../resource/before/NonNullWithSneakyThrows.java | 5 + 14 files changed, 407 insertions(+), 313 deletions(-) create mode 100644 src/core/lombok/eclipse/handlers/HandleNonNull.java delete mode 100644 src/core/lombok/eclipse/handlers/NonNullHandler.java create mode 100644 src/core/lombok/javac/handlers/HandleNonNull.java delete mode 100644 src/core/lombok/javac/handlers/NonNullHandler.java create mode 100644 test/transform/resource/after-delombok/NonNullWithSneakyThrows.java create mode 100644 test/transform/resource/after-ecj/NonNullWithSneakyThrows.java create mode 100644 test/transform/resource/before/NonNullWithSneakyThrows.java (limited to 'src/core/lombok/eclipse/handlers') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 7782f1fa..1ee3504c 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -1,8 +1,13 @@ Lombok Changelog ---------------- -### v0.12.1 "Edgy Guinea Pig" -* PLATFORM: Initial (not quite totally finished) JDK8 support, without affecting existing support for JDK6 and 7. [Issue #451](https://code.google.com/p/projectlombok/issues/detail?id=451). +### v1.12.2 (October 10th, 2013) +* PLATFORM: Initial JDK8 support, without affecting existing support for JDK6 and 7. [Issue #451](https://code.google.com/p/projectlombok/issues/detail?id=451). While lombok will now work on JDK8 / javac8, and netbeans 7.4 and up, lombok does not (yet) support new language features introduced with java8, such as lambda expressions. Support for these features will be added in a future version. +* PLATFORM: Running javac on IBM J9 VM would cause NullPointerExceptions when compiling with lombok. These issues should be fixed. [Issue #554](https://code.google.com/p/projectlombok/issues/detail?id=554). +* CHANGE: [JDK8-related] The canonical way to write onMethod / onParameter / onConstructor annotation now uses a double underscore instead of a single underscore, so, now, the proper way to use this feature is `@RequiredArgsConstructor(onConstructor=@__(@Inject))`. The old way (single underscore) still works, but generates warnings on javac 8. +* BUGFIX: Using `@NonNull` on an abstract method used to cause exceptions during compilation. [Issue #559](https://code.google.com/p/projectlombok/issues/detail?id=559). +* BUGFIX: Using `@NonNull` on methods that also have `@SneakyThrows` or `@Synchronized` caused arbitrary behaviour. [Issue #588](https://code.google.com/p/projectlombok/issues/detail?id=588). +* GERMANY: Major version bumped from 0 to 1, because allegedly this is important. Rest assured, this change is nevertheless backwards compatible. ### v0.12.0 "Angry Butterfly" (July 16th, 2013) * FEATURE: javadoc on fields will now be copied to generated getters / setters / withers. There are ways to specify separate javadoc for the field, the setter, and the getter, and `@param` and `@return` are handled appropriately. Addresses feature request [Issue #59](https://code.google.com/p/projectlombok/issues/detail?id=59). [@Getter and @Setter documentation](http://projectlombok.org/features/GetterSetter.html). [@Wither documentation](http://projectlombok.org/features/experimental/Wither.html). diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index 24faf821..318075d3 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 The Project Lombok Authors. + * Copyright (C) 2009-2013 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 diff --git a/src/core/lombok/eclipse/handlers/HandleNonNull.java b/src/core/lombok/eclipse/handlers/HandleNonNull.java new file mode 100644 index 00000000..634cb2d9 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleNonNull.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse.handlers; + +import static lombok.eclipse.Eclipse.isPrimitive; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +import java.util.Arrays; + +import lombok.NonNull; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.eclipse.DeferUntilPostDiet; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.Block; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.IfStatement; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +import org.eclipse.jdt.internal.compiler.ast.OperatorIds; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement; +import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; +import org.eclipse.jdt.internal.compiler.ast.TryStatement; +import org.mangosdk.spi.ProviderFor; + +@DeferUntilPostDiet +@ProviderFor(EclipseAnnotationHandler.class) +@HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. +public class HandleNonNull extends EclipseAnnotationHandler { + @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { + if (annotationNode.up().getKind() == Kind.FIELD) { + // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), + // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to + // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning + // behaviour on _OUR_ 'lombok.NonNull'. + + try { + if (isPrimitive(((AbstractVariableDeclaration) annotationNode.up().get()).type)) { + annotationNode.addWarning("@NonNull is meaningless on a primitive."); + } + } catch (Exception ignore) {} + + return; + } + + if (annotationNode.up().getKind() != Kind.ARGUMENT) return; + + Argument arg; + AbstractMethodDeclaration declaration; + + try { + arg = (Argument) annotationNode.up().get(); + declaration = (AbstractMethodDeclaration) annotationNode.up().up().get(); + } catch (Exception e) { + return; + } + + if (isGenerated(declaration)) return; + + if (declaration.isAbstract()) { + annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method."); + return; + } + + // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, + // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and + // wrap all references to it in the super/this to a call to this method. + + Statement nullCheck = generateNullCheck(arg, ast); + + if (nullCheck == null) { + // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning. + annotationNode.addWarning("@NonNull is meaningless on a primitive."); + return; + } + + if (declaration.statements == null) { + declaration.statements = new Statement[] {nullCheck}; + } else { + char[] expectedName = arg.name; + /* Abort if the null check is already there, delving into try and synchronized statements */ { + Statement[] stats = declaration.statements; + int idx = 0; + while (stats != null && stats.length > idx) { + Statement stat = stats[idx++]; + if (stat instanceof TryStatement) { + stats = ((TryStatement) stat).tryBlock.statements; + idx = 0; + continue; + } + if (stat instanceof SynchronizedStatement) { + stats = ((SynchronizedStatement) stat).block.statements; + idx = 0; + continue; + } + char[] varNameOfNullCheck = returnVarNameIfNullCheck(stat); + if (varNameOfNullCheck == null) break; + if (Arrays.equals(varNameOfNullCheck, expectedName)) return; + } + } + + Statement[] newStatements = new Statement[declaration.statements.length + 1]; + int skipOver = 0; + for (Statement stat : declaration.statements) { + if (isGenerated(stat) && isNullCheck(stat)) skipOver++; + else break; + } + System.arraycopy(declaration.statements, 0, newStatements, 0, skipOver); + System.arraycopy(declaration.statements, skipOver, newStatements, skipOver + 1, declaration.statements.length - skipOver); + newStatements[skipOver] = nullCheck; + declaration.statements = newStatements; + } + annotationNode.up().up().rebuild(); + } + + private boolean isNullCheck(Statement stat) { + return returnVarNameIfNullCheck(stat) != null; + } + + private char[] returnVarNameIfNullCheck(Statement stat) { + if (!(stat instanceof IfStatement)) return null; + + /* Check that the if's statement is a throw statement, possibly in a block. */ { + Statement then = ((IfStatement) stat).thenStatement; + if (then instanceof Block) { + Statement[] blockStatements = ((Block) then).statements; + if (blockStatements == null || blockStatements.length == 0) return null; + then = blockStatements[0]; + } + + if (!(then instanceof ThrowStatement)) return null; + } + + /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate + a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ { + Expression cond = ((IfStatement) stat).condition; + if (!(cond instanceof EqualExpression)) return null; + EqualExpression bin = (EqualExpression) cond; + int operatorId = ((bin.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT); + if (operatorId != OperatorIds.EQUAL_EQUAL) return null; + if (!(bin.left instanceof SingleNameReference)) return null; + if (!(bin.right instanceof NullLiteral)) return null; + return ((SingleNameReference) bin.left).token; + } + } +} diff --git a/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java index aa78ca3b..d3a95db8 100644 --- a/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java +++ b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java @@ -30,6 +30,7 @@ import java.util.List; import lombok.SneakyThrows; import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -60,6 +61,7 @@ import org.mangosdk.spi.ProviderFor; */ @ProviderFor(EclipseAnnotationHandler.class) @DeferUntilPostDiet +@HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSneakyThrows extends EclipseAnnotationHandler { private static class DeclaredException { diff --git a/src/core/lombok/eclipse/handlers/HandleSynchronized.java b/src/core/lombok/eclipse/handlers/HandleSynchronized.java index e4c58eab..f76f06ed 100644 --- a/src/core/lombok/eclipse/handlers/HandleSynchronized.java +++ b/src/core/lombok/eclipse/handlers/HandleSynchronized.java @@ -27,6 +27,7 @@ import java.lang.reflect.Modifier; import lombok.Synchronized; import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; import lombok.core.AST.Kind; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseAnnotationHandler; @@ -52,6 +53,7 @@ import org.mangosdk.spi.ProviderFor; */ @ProviderFor(EclipseAnnotationHandler.class) @DeferUntilPostDiet +@HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSynchronized extends EclipseAnnotationHandler { private static final char[] INSTANCE_LOCK_NAME = "$lock".toCharArray(); private static final char[] STATIC_LOCK_NAME = "$LOCK".toCharArray(); diff --git a/src/core/lombok/eclipse/handlers/NonNullHandler.java b/src/core/lombok/eclipse/handlers/NonNullHandler.java deleted file mode 100644 index 59fda801..00000000 --- a/src/core/lombok/eclipse/handlers/NonNullHandler.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2013 The Project Lombok Authors. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package lombok.eclipse.handlers; - -import java.util.Arrays; - -import org.eclipse.jdt.internal.compiler.ast.ASTNode; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; -import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; -import org.eclipse.jdt.internal.compiler.ast.Annotation; -import org.eclipse.jdt.internal.compiler.ast.Argument; -import org.eclipse.jdt.internal.compiler.ast.Block; -import org.eclipse.jdt.internal.compiler.ast.EqualExpression; -import org.eclipse.jdt.internal.compiler.ast.Expression; -import org.eclipse.jdt.internal.compiler.ast.IfStatement; -import org.eclipse.jdt.internal.compiler.ast.NullLiteral; -import org.eclipse.jdt.internal.compiler.ast.OperatorIds; -import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; -import org.eclipse.jdt.internal.compiler.ast.Statement; -import org.eclipse.jdt.internal.compiler.ast.ThrowStatement; -import org.mangosdk.spi.ProviderFor; - -import lombok.NonNull; -import lombok.core.AST.Kind; -import lombok.core.AnnotationValues; -import lombok.eclipse.DeferUntilPostDiet; -import lombok.eclipse.EclipseAnnotationHandler; -import lombok.eclipse.EclipseNode; - -import static lombok.eclipse.Eclipse.*; -import static lombok.eclipse.handlers.EclipseHandlerUtil.*; - -@DeferUntilPostDiet -@ProviderFor(EclipseAnnotationHandler.class) -public class NonNullHandler extends EclipseAnnotationHandler { - @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { - if (annotationNode.up().getKind() == Kind.FIELD) { - // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), - // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to - // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning - // behaviour on _OUR_ 'lombok.NonNull'. - - try { - if (isPrimitive(((AbstractVariableDeclaration) annotationNode.up().get()).type)) { - annotationNode.addWarning("@NonNull is meaningless on a primitive."); - } - } catch (Exception ignore) {} - - return; - } - - if (annotationNode.up().getKind() != Kind.ARGUMENT) return; - - Argument arg; - AbstractMethodDeclaration declaration; - - try { - arg = (Argument) annotationNode.up().get(); - declaration = (AbstractMethodDeclaration) annotationNode.up().up().get(); - } catch (Exception e) { - return; - } - - if (isGenerated(declaration)) return; - - if (declaration.isAbstract()) { - annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method."); - return; - } - - // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, - // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and - // wrap all references to it in the super/this to a call to this method. - - Statement nullCheck = generateNullCheck(arg, ast); - - if (nullCheck == null) { - // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning. - annotationNode.addWarning("@NonNull is meaningless on a primitive."); - return; - } - - if (declaration.statements == null) { - declaration.statements = new Statement[] {nullCheck}; - } else { - char[] expectedName = arg.name; - for (Statement stat : declaration.statements) { - char[] varNameOfNullCheck = returnVarNameIfNullCheck(stat); - if (varNameOfNullCheck == null) break; - if (Arrays.equals(expectedName, varNameOfNullCheck)) return; - } - - Statement[] newStatements = new Statement[declaration.statements.length + 1]; - int skipOver = 0; - for (Statement stat : declaration.statements) { - if (isGenerated(stat)) skipOver++; - else break; - } - System.arraycopy(declaration.statements, 0, newStatements, 0, skipOver); - System.arraycopy(declaration.statements, skipOver, newStatements, skipOver + 1, declaration.statements.length - skipOver); - newStatements[skipOver] = nullCheck; - declaration.statements = newStatements; - } - annotationNode.up().up().rebuild(); - } - - private char[] returnVarNameIfNullCheck(Statement stat) { - if (!(stat instanceof IfStatement)) return null; - - /* Check that the if's statement is a throw statement, possibly in a block. */ { - Statement then = ((IfStatement) stat).thenStatement; - if (then instanceof Block) { - Statement[] blockStatements = ((Block) then).statements; - if (blockStatements == null || blockStatements.length == 0) return null; - then = blockStatements[0]; - } - - if (!(then instanceof ThrowStatement)) return null; - } - - /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate - a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ { - Expression cond = ((IfStatement) stat).condition; - if (!(cond instanceof EqualExpression)) return null; - EqualExpression bin = (EqualExpression) cond; - int operatorId = ((bin.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT); - if (operatorId != OperatorIds.EQUAL_EQUAL) return null; - if (!(bin.left instanceof SingleNameReference)) return null; - if (!(bin.right instanceof NullLiteral)) return null; - return ((SingleNameReference) bin.left).token; - } - } -} diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java new file mode 100644 index 00000000..21611a39 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleNonNull.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.handlers; + +import static lombok.javac.Javac.*; +import static lombok.javac.handlers.JavacHandlerUtil.*; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCBinary; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCIf; +import com.sun.tools.javac.tree.JCTree.JCLiteral; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCParens; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCSynchronized; +import com.sun.tools.javac.tree.JCTree.JCThrow; +import com.sun.tools.javac.tree.JCTree.JCTry; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.List; + +import lombok.NonNull; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.core.AST.Kind; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import static lombok.javac.JavacTreeMaker.TypeTag.*; +import static lombok.javac.JavacTreeMaker.TreeTag.*; + +@ProviderFor(JavacAnnotationHandler.class) +@HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. +public class HandleNonNull extends JavacAnnotationHandler { + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { + if (annotationNode.up().getKind() == Kind.FIELD) { + // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), + // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to + // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning + // behaviour on _OUR_ 'lombok.NonNull'. + + try { + if (isPrimitive(((JCVariableDecl) annotationNode.up().get()).vartype)) { + annotationNode.addWarning("@NonNull is meaningless on a primitive."); + } + } catch (Exception ignore) {} + + return; + } + + if (annotationNode.up().getKind() != Kind.ARGUMENT) return; + + JCMethodDecl declaration; + + try { + declaration = (JCMethodDecl) annotationNode.up().up().get(); + } catch (Exception e) { + return; + } + +// if (JavacHandlerUtil.isGenerated(declaration)) return; + + if (declaration.body == null) { + annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method."); + return; + } + + // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, + // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and + // wrap all references to it in the super/this to a call to this method. + + JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), annotationNode.up()), ast); + + if (nullCheck == null) { + // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning. + annotationNode.addWarning("@NonNull is meaningless on a primitive."); + return; + } + + List statements = declaration.body.stats; + + String expectedName = annotationNode.up().getName(); + + /* Abort if the null check is already there, delving into try and synchronized statements */ { + List stats = statements; + int idx = 0; + while (stats.size() > idx) { + JCStatement stat = stats.get(idx++); + if (JavacHandlerUtil.isConstructorCall(stat)) continue; + if (stat instanceof JCTry) { + stats = ((JCTry) stat).body.stats; + idx = 0; + continue; + } + if (stat instanceof JCSynchronized) { + stats = ((JCSynchronized) stat).body.stats; + idx = 0; + continue; + } + String varNameOfNullCheck = returnVarNameIfNullCheck(stat); + if (varNameOfNullCheck == null) break; + if (varNameOfNullCheck.equals(expectedName)) return; + } + } + + List tail = statements; + List head = List.nil(); + for (JCStatement stat : statements) { + if (JavacHandlerUtil.isConstructorCall(stat) || (JavacHandlerUtil.isGenerated(stat) && isNullCheck(stat))) { + tail = tail.tail; + head = head.prepend(stat); + continue; + } + break; + } + + List newList = tail.prepend(nullCheck); + for (JCStatement stat : head) newList = newList.prepend(stat); + declaration.body.stats = newList; + } + + private boolean isNullCheck(JCStatement stat) { + return returnVarNameIfNullCheck(stat) != null; + } + + /** + * Checks if the statement is of the form 'if (x == null) {throw WHATEVER;}, + * where the block braces are optional. If it is of this form, returns "x". + * If it is not of this form, returns null. + */ + private String returnVarNameIfNullCheck(JCStatement stat) { + if (!(stat instanceof JCIf)) return null; + + /* Check that the if's statement is a throw statement, possibly in a block. */ { + JCStatement then = ((JCIf) stat).thenpart; + if (then instanceof JCBlock) { + List stats = ((JCBlock) then).stats; + if (stats.length() == 0) return null; + then = stats.get(0); + } + if (!(then instanceof JCThrow)) return null; + } + + /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate + a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ { + JCExpression cond = ((JCIf) stat).cond; + while (cond instanceof JCParens) cond = ((JCParens) cond).expr; + if (!(cond instanceof JCBinary)) return null; + JCBinary bin = (JCBinary) cond; + if (!CTC_EQUAL.equals(treeTag(bin))) return null; + if (!(bin.lhs instanceof JCIdent)) return null; + if (!(bin.rhs instanceof JCLiteral)) return null; + if (!CTC_BOT.equals(typeTag(bin.rhs))) return null; + return ((JCIdent) bin.lhs).name.toString(); + } + } +} diff --git a/src/core/lombok/javac/handlers/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java index 69d2b45d..b41277c3 100644 --- a/src/core/lombok/javac/handlers/HandleSneakyThrows.java +++ b/src/core/lombok/javac/handlers/HandleSneakyThrows.java @@ -29,6 +29,7 @@ import java.util.Collections; import lombok.SneakyThrows; import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -49,6 +50,7 @@ import com.sun.tools.javac.util.List; * Handles the {@code lombok.SneakyThrows} annotation for javac. */ @ProviderFor(JavacAnnotationHandler.class) +@HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSneakyThrows extends JavacAnnotationHandler { @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { deleteAnnotationIfNeccessary(annotationNode, SneakyThrows.class); diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java index b173f8fb..661a7c2a 100644 --- a/src/core/lombok/javac/handlers/HandleSynchronized.java +++ b/src/core/lombok/javac/handlers/HandleSynchronized.java @@ -26,6 +26,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*; import lombok.Synchronized; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -46,6 +47,7 @@ import com.sun.tools.javac.util.List; * Handles the {@code lombok.Synchronized} annotation for javac. */ @ProviderFor(JavacAnnotationHandler.class) +@HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSynchronized extends JavacAnnotationHandler { private static final String INSTANCE_LOCK_NAME = "$lock"; private static final String STATIC_LOCK_NAME = "$LOCK"; diff --git a/src/core/lombok/javac/handlers/NonNullHandler.java b/src/core/lombok/javac/handlers/NonNullHandler.java deleted file mode 100644 index acf1588e..00000000 --- a/src/core/lombok/javac/handlers/NonNullHandler.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2013 The Project Lombok Authors. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package lombok.javac.handlers; - -import static lombok.javac.Javac.*; -import static lombok.javac.handlers.JavacHandlerUtil.*; - -import org.mangosdk.spi.ProviderFor; - -import com.sun.tools.javac.tree.JCTree.JCAnnotation; -import com.sun.tools.javac.tree.JCTree.JCBinary; -import com.sun.tools.javac.tree.JCTree.JCBlock; -import com.sun.tools.javac.tree.JCTree.JCExpression; -import com.sun.tools.javac.tree.JCTree.JCIdent; -import com.sun.tools.javac.tree.JCTree.JCIf; -import com.sun.tools.javac.tree.JCTree.JCLiteral; -import com.sun.tools.javac.tree.JCTree.JCMethodDecl; -import com.sun.tools.javac.tree.JCTree.JCParens; -import com.sun.tools.javac.tree.JCTree.JCStatement; -import com.sun.tools.javac.tree.JCTree.JCThrow; -import com.sun.tools.javac.tree.JCTree.JCVariableDecl; -import com.sun.tools.javac.util.List; - -import lombok.NonNull; -import lombok.core.AnnotationValues; -import lombok.core.AST.Kind; -import lombok.javac.JavacAnnotationHandler; -import lombok.javac.JavacNode; - -import static lombok.javac.JavacTreeMaker.TypeTag.*; -import static lombok.javac.JavacTreeMaker.TreeTag.*; - -@ProviderFor(JavacAnnotationHandler.class) -public class NonNullHandler extends JavacAnnotationHandler { - @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { - if (annotationNode.up().getKind() == Kind.FIELD) { - // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), - // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to - // a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning - // behaviour on _OUR_ 'lombok.NonNull'. - - try { - if (isPrimitive(((JCVariableDecl) annotationNode.up().get()).vartype)) { - annotationNode.addWarning("@NonNull is meaningless on a primitive."); - } - } catch (Exception ignore) {} - - return; - } - - if (annotationNode.up().getKind() != Kind.ARGUMENT) return; - - JCMethodDecl declaration; - - try { - declaration = (JCMethodDecl) annotationNode.up().up().get(); - } catch (Exception e) { - return; - } - - if (JavacHandlerUtil.isGenerated(declaration)) return; - - if (declaration.body == null) { - annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method."); - return; - } - - // Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter, - // and if they exist, create a new method in the class: 'private static T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and - // wrap all references to it in the super/this to a call to this method. - - JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), annotationNode.up()), ast); - - if (nullCheck == null) { - // @NonNull applied to a primitive. Kinda pointless. Let's generate a warning. - annotationNode.addWarning("@NonNull is meaningless on a primitive."); - return; - } - - List statements = declaration.body.stats; - - String expectedName = annotationNode.up().getName(); - for (JCStatement stat : statements) { - if (JavacHandlerUtil.isConstructorCall(stat)) continue; - String varNameOfNullCheck = returnVarNameIfNullCheck(stat); - if (varNameOfNullCheck == null) break; - if (varNameOfNullCheck.equals(expectedName)) return; - } - - List tail = statements; - List head = List.nil(); - for (JCStatement stat : statements) { - if (JavacHandlerUtil.isConstructorCall(stat) || JavacHandlerUtil.isGenerated(stat)) { - tail = tail.tail; - head = head.prepend(stat); - continue; - } - break; - } - - List newList = tail.prepend(nullCheck); - for (JCStatement stat : head) newList = newList.prepend(stat); - declaration.body.stats = newList; - } - - /** - * Checks if the statement is of the form 'if (x == null) {throw WHATEVER;}, - * where the block braces are optional. If it is of this form, returns "x". - * If it is not of this form, returns null. - */ - private String returnVarNameIfNullCheck(JCStatement stat) { - if (!(stat instanceof JCIf)) return null; - - /* Check that the if's statement is a throw statement, possibly in a block. */ { - JCStatement then = ((JCIf) stat).thenpart; - if (then instanceof JCBlock) { - List stats = ((JCBlock) then).stats; - if (stats.length() == 0) return null; - then = stats.get(0); - } - if (!(then instanceof JCThrow)) return null; - } - - /* Check that the if's conditional is like 'x == null'. Return from this method (don't generate - a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ { - JCExpression cond = ((JCIf) stat).cond; - while (cond instanceof JCParens) cond = ((JCParens) cond).expr; - if (!(cond instanceof JCBinary)) return null; - JCBinary bin = (JCBinary) cond; - if (!CTC_EQUAL.equals(treeTag(bin))) return null; - if (!(bin.lhs instanceof JCIdent)) return null; - if (!(bin.rhs instanceof JCLiteral)) return null; - if (!CTC_BOT.equals(typeTag(bin.rhs))) return null; - return ((JCIdent) bin.lhs).name.toString(); - } - } -} diff --git a/test/transform/resource/after-delombok/NonNullWithSneakyThrows.java b/test/transform/resource/after-delombok/NonNullWithSneakyThrows.java new file mode 100644 index 00000000..91646468 --- /dev/null +++ b/test/transform/resource/after-delombok/NonNullWithSneakyThrows.java @@ -0,0 +1,12 @@ +class NonNullWithSneakyThrows { + void test(@lombok.NonNull String in) { + try { + if (in == null) { + throw new java.lang.NullPointerException("in"); + } + System.out.println(in); + } catch (final java.lang.Throwable $ex) { + throw lombok.Lombok.sneakyThrow($ex); + } + } +} \ No newline at end of file diff --git a/test/transform/resource/after-ecj/InjectField.java b/test/transform/resource/after-ecj/InjectField.java index 83d9e5fa..f9ea9142 100644 --- a/test/transform/resource/after-ecj/InjectField.java +++ b/test/transform/resource/after-ecj/InjectField.java @@ -4,9 +4,9 @@ import lombok.Synchronized; @Log enum InjectField1 { A(), B(), + private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(InjectField1.class.getName()); private final java.lang.Object $lock = new java.lang.Object[0]; private static final java.lang.Object $LOCK = new java.lang.Object[0]; - private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(InjectField1.class.getName()); private static final String LOG_MESSAGE = "static initializer"; private String fieldA; static { @@ -32,8 +32,8 @@ import lombok.Synchronized; } } @Log class InjectField2 { - private final java.lang.Object $lock = new java.lang.Object[0]; private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(InjectField2.class.getName()); + private final java.lang.Object $lock = new java.lang.Object[0]; private static final String LOG_MESSAGE = "static initializer"; static { log.log(Level.FINE, LOG_MESSAGE); diff --git a/test/transform/resource/after-ecj/NonNullWithSneakyThrows.java b/test/transform/resource/after-ecj/NonNullWithSneakyThrows.java new file mode 100644 index 00000000..fac8dcdd --- /dev/null +++ b/test/transform/resource/after-ecj/NonNullWithSneakyThrows.java @@ -0,0 +1,18 @@ +class NonNullWithSneakyThrows { + NonNullWithSneakyThrows() { + super(); + } + @lombok.SneakyThrows void test(@lombok.NonNull String in) { + try + { + if ((in == null)) + { + throw new java.lang.NullPointerException("in"); + } + System.out.println(in); + } + catch (final java.lang.Throwable $ex) { + throw lombok.Lombok.sneakyThrow($ex); + } + } +} \ No newline at end of file diff --git a/test/transform/resource/before/NonNullWithSneakyThrows.java b/test/transform/resource/before/NonNullWithSneakyThrows.java new file mode 100644 index 00000000..35f78a7f --- /dev/null +++ b/test/transform/resource/before/NonNullWithSneakyThrows.java @@ -0,0 +1,5 @@ +class NonNullWithSneakyThrows { + @lombok.SneakyThrows void test(@lombok.NonNull String in) { + System.out.println(in); + } +} -- cgit