mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-11 12:19:00 +00:00
[fixes #788] lombok generated equals method plus a non-null-by-default annotation no longer clash.
This commit is contained in:
13 files changed
+160
-6
No files matched your search
@@ -4,6 +4,7 @@ Lombok Changelog
|
||||
### v1.18.11 "Edgy Guinea Pig"
|
||||
* PLATFORM: Support for JDK13 (including `yield` in switch expressions, as well as delombok having a nicer style for arrow-style switch blocks, and text blocks).
|
||||
* FEATURE: You can now configure a builder's 'setter' prefixes via `@Builder(setterPrefix = "set")` for example. We discourage doing this, but if some library you use requires them, have at it. [Pull Request #2174](https://github.com/rzwitserloot/lombok/pull/2174], [Issue #1805](https://github.com/rzwitserloot/lombok/issues/1805).
|
||||
* FEATURE: Tired of being unable to use `@javax.annotation.ParametersAreNonnullByDefault` or `@org.eclipse.jdt.annotation.NonNullByDefault` because then the equals method that lombok generates isn't valid? Fret no more; lombok will now add the appropriate `@Nullable` annotation to the parameter to avoid any issues. (package-level nonnullByDefault not supported, it must appear on the class for this feature to work). [Issue #788](https://github.com/rzwitserloot/lombok/issues/788)
|
||||
* BUGFIX: Referring to an inner class inside the generics on a class marked with `@SuperBuilder` would cause the error `wrong number of type arguments; required 3` [Issue #2262](https://github.com/rzwitserloot/lombok/issues/2262); fixed by github user [`@Lekanich`](https://github.com/rzwitserloot/lombok/issues/2262) - thank you!
|
||||
* BUGFIX: Some of the code generated by `@Builder` did not include `this.` prefixes when accessing fields. While semantically it didn't matter, if you use the 'add this prefix for field accesses' save action in eclipse, the save action would break. [Issue #2327](https://github.com/rzwitserloot/lombok/issues/2327)
|
||||
* BUGFIX: When lombok copies javadoc from fields to relevant methods, it should generate an appropriate `@return this` line if lombok copies the javadoc to a generated setter that is chainable (returns itself). It didn't do that when generating the 'setters' in a `@Builder`. Lombok also didn't generate an appropriate `@return` item for `@With` methods. The javadoc has also been updated slightly (the `this` reference in the javadoc is now rendered in a code tag).[Issue #2323](https://github.com/rzwitserloot/lombok/issues/2323)
|
||||
|
||||
@@ -745,6 +745,20 @@ public class EclipseHandlerUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String scanForNearestAnnotation(EclipseNode node, String... anns) {
|
||||
while (node != null) {
|
||||
for (EclipseNode ann : node.down()) {
|
||||
if (ann.getKind() != Kind.ANNOTATION) continue;
|
||||
Annotation a = (Annotation) ann.get();
|
||||
TypeReference aType = a.type;
|
||||
for (String annToFind : anns) if (typeMatches(annToFind, node, aType)) return annToFind;
|
||||
}
|
||||
node = node.up();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean hasNonNullAnnotations(EclipseNode node) {
|
||||
AbstractVariableDeclaration avd = (AbstractVariableDeclaration) node.get();
|
||||
if (avd.annotations == null) return false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2019 The Project Lombok Authors.
|
||||
* Copyright (C) 2009-2020 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
|
||||
@@ -63,6 +63,7 @@ import org.eclipse.jdt.internal.compiler.ast.IfStatement;
|
||||
import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
|
||||
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
|
||||
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.NameReference;
|
||||
@@ -504,9 +505,33 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
|
||||
return arr == null ? 0 : arr.length;
|
||||
}
|
||||
|
||||
/*
|
||||
* scan method, then class, then enclosing classes, then package for the first of:
|
||||
* javax.annotation.ParametersAreNonnullByDefault or javax.annotation.ParametersAreNullableByDefault. If it's the NN variant, generate javax.annotation.Nullable _on the type_.
|
||||
* org.eclipse.jdt.annotation.NonNullByDefault -> org.eclipse.jdt.annotation.Nullable
|
||||
*/
|
||||
|
||||
private static final char[][] JAVAX_ANNOTATION_NULLABLE = Eclipse.fromQualifiedName("javax.annotation.Nullable");
|
||||
private static final char[][] ORG_ECLIPSE_JDT_ANNOTATION_NULLABLE = Eclipse.fromQualifiedName("org.eclipse.jdt.annotation.Nullable");
|
||||
|
||||
public MethodDeclaration createEquals(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> onParam) {
|
||||
int pS = source.sourceStart; int pE = source.sourceEnd;
|
||||
long p = (long)pS << 32 | pE;
|
||||
long p = (long) pS << 32 | pE;
|
||||
|
||||
Annotation[] onParamType = null;
|
||||
|
||||
String nearest = scanForNearestAnnotation(type, "javax.annotation.ParametersAreNullableByDefault", "javax.annotation.ParametersAreNonnullByDefault");
|
||||
if ("javax.annotation.ParametersAreNonnullByDefault".equals(nearest)) {
|
||||
onParamType = new Annotation[1];
|
||||
onParamType[0] = new MarkerAnnotation(generateQualifiedTypeRef(source, JAVAX_ANNOTATION_NULLABLE), 0);
|
||||
}
|
||||
|
||||
nearest = scanForNearestAnnotation(type, "org.eclipse.jdt.annotation.NonNullByDefault");
|
||||
if (nearest != null) {
|
||||
Annotation a = new MarkerAnnotation(generateQualifiedTypeRef(source, ORG_ECLIPSE_JDT_ANNOTATION_NULLABLE), 0);
|
||||
if (onParamType != null) onParamType = new Annotation[] {onParamType[0], a};
|
||||
else onParamType = new Annotation[] {a};
|
||||
}
|
||||
|
||||
MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
|
||||
setGeneratedBy(method, source);
|
||||
@@ -526,7 +551,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
|
||||
method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
|
||||
method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
|
||||
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
|
||||
TypeReference objectRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, new long[] { p, p, p });
|
||||
QualifiedTypeReference objectRef = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, new long[] { p, p, p });
|
||||
if (onParamType != null) objectRef.annotations = new Annotation[][] {null, null, onParamType};
|
||||
setGeneratedBy(objectRef, source);
|
||||
method.arguments = new Argument[] {new Argument(new char[] { 'o' }, 0, objectRef, Modifier.FINAL)};
|
||||
method.arguments[0].sourceStart = pS; method.arguments[0].sourceEnd = pE;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2019 The Project Lombok Authors.
|
||||
* Copyright (C) 2009-2020 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
|
||||
@@ -381,6 +381,20 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
|
||||
Name otherName = typeNode.toName("other");
|
||||
Name thisName = typeNode.toName("this");
|
||||
|
||||
List<JCAnnotation> annsOnParamOnMethod = List.nil();
|
||||
|
||||
String nearest = scanForNearestAnnotation(typeNode, "org.eclipse.jdt.annotation.NonNullByDefault");
|
||||
if (nearest != null) {
|
||||
JCAnnotation m = maker.Annotation(genTypeRef(typeNode, "org.eclipse.jdt.annotation.Nullable"), List.<JCExpression>nil());
|
||||
annsOnParamOnMethod = annsOnParamOnMethod.prepend(m);
|
||||
}
|
||||
|
||||
nearest = scanForNearestAnnotation(typeNode, "javax.annotation.ParametersAreNullableByDefault", "javax.annotation.ParametersAreNonnullByDefault");
|
||||
if ("javax.annotation.ParametersAreNonnullByDefault".equals(nearest)) {
|
||||
JCAnnotation m = maker.Annotation(genTypeRef(typeNode, "javax.annotation.Nullable"), List.<JCExpression>nil());
|
||||
annsOnParamOnMethod = annsOnParamOnMethod.prepend(m);
|
||||
}
|
||||
|
||||
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
|
||||
List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation);
|
||||
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode);
|
||||
@@ -388,7 +402,14 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
|
||||
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
|
||||
}
|
||||
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
|
||||
JCExpression objectType = genJavaLangTypeRef(typeNode, "Object");
|
||||
JCExpression objectType;
|
||||
if (annsOnParamOnMethod.isEmpty()) {
|
||||
objectType = genJavaLangTypeRef(typeNode, "Object");
|
||||
} else {
|
||||
objectType = chainDots(typeNode, "java", "lang", "Object");
|
||||
objectType = maker.AnnotatedType(annsOnParamOnMethod, objectType);
|
||||
}
|
||||
|
||||
JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN);
|
||||
|
||||
long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());
|
||||
|
||||
@@ -1400,6 +1400,19 @@ public class JavacHandlerUtil {
|
||||
return result.toList();
|
||||
}
|
||||
|
||||
public static String scanForNearestAnnotation(JavacNode node, String... anns) {
|
||||
while (node != null) {
|
||||
for (JavacNode ann : node.down()) {
|
||||
if (ann.getKind() != Kind.ANNOTATION) continue;
|
||||
JCAnnotation a = (JCAnnotation) ann.get();
|
||||
for (String annToFind : anns) if (typeMatches(annToFind, node, a.annotationType)) return annToFind;
|
||||
}
|
||||
node = node.up();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean hasNonNullAnnotations(JavacNode node) {
|
||||
for (JavacNode child : node.down()) {
|
||||
if (child.getKind() == Kind.ANNOTATION) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2018 The Project Lombok Authors.
|
||||
* Copyright (C) 2013-2020 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
|
||||
@@ -864,6 +864,12 @@ public class JavacTreeMaker {
|
||||
return invoke(TypeAnnotationWithAttributeOnly, a);
|
||||
}
|
||||
|
||||
//javac versions: 8
|
||||
private static final MethodId<JCExpression> AnnotatedType = MethodId("AnnotatedType", JCExpression.class, List.class, JCExpression.class);
|
||||
public JCExpression AnnotatedType(List<JCAnnotation> annotations, JCExpression underlyingType) {
|
||||
return invoke(AnnotatedType, annotations, underlyingType);
|
||||
}
|
||||
|
||||
//javac versions: 6-8
|
||||
private static final MethodId<JCStatement> Call = MethodId("Call");
|
||||
public JCStatement Call(JCExpression apply) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
class EqualsAndHashCodeWithNNBD {
|
||||
|
||||
@org.eclipse.jdt.annotation.NonNullByDefault
|
||||
static class Inner {
|
||||
@java.lang.Override
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public boolean equals(final java.lang.@javax.annotation.Nullable @org.eclipse.jdt.annotation.Nullable Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof EqualsAndHashCodeWithNNBD.Inner)) return false;
|
||||
final EqualsAndHashCodeWithNNBD.Inner other = (EqualsAndHashCodeWithNNBD.Inner) o;
|
||||
if (!other.canEqual((java.lang.Object) this)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@java.lang.SuppressWarnings("all")
|
||||
protected boolean canEqual(final java.lang.Object other) {
|
||||
return other instanceof EqualsAndHashCodeWithNNBD.Inner;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
@java.lang.SuppressWarnings("all")
|
||||
public int hashCode() {
|
||||
final int result = 1;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@ParametersAreNonnullByDefault class EqualsAndHashCodeWithNNBD {
|
||||
static @lombok.EqualsAndHashCode @org.eclipse.jdt.annotation.NonNullByDefault class Inner {
|
||||
Inner() {
|
||||
super();
|
||||
}
|
||||
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.@javax.annotation.Nullable @org.eclipse.jdt.annotation.Nullable Object o) {
|
||||
if ((o == this))
|
||||
return true;
|
||||
if ((! (o instanceof EqualsAndHashCodeWithNNBD.Inner)))
|
||||
return false;
|
||||
final EqualsAndHashCodeWithNNBD.Inner other = (EqualsAndHashCodeWithNNBD.Inner) o;
|
||||
if ((! other.canEqual((java.lang.Object) this)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
protected @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
|
||||
return (other instanceof EqualsAndHashCodeWithNNBD.Inner);
|
||||
}
|
||||
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
|
||||
final int result = 1;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
EqualsAndHashCodeWithNNBD() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// version 8:
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@ParametersAreNonnullByDefault
|
||||
class EqualsAndHashCodeWithNNBD {
|
||||
@lombok.EqualsAndHashCode @org.eclipse.jdt.annotation.NonNullByDefault
|
||||
static class Inner {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
5 package org.eclipse.jdt.annotation does not exist
|
||||
@@ -0,0 +1 @@
|
||||
2 The import javax.annotation.ParametersAreNonnullByDefault cannot be resolved
|
||||
@@ -0,0 +1,3 @@
|
||||
6 package org.eclipse.jdt.annotation does not exist
|
||||
10 package org.eclipse.jdt.annotation does not exist
|
||||
10 annotation @javax.annotation.Nullable not applicable in this type context
|
||||
@@ -53,6 +53,8 @@
|
||||
</p><p>
|
||||
If a getter exists for a field to be included, it is called instead of using a direct field reference. This behaviour can be suppressed:<br />
|
||||
<code>@EqualsAndHashCode(doNotUseGetters = true)</code>
|
||||
</p><p>
|
||||
If the class (or an enclosing class) has either the <code>@org.eclipse.jdt.annotation.NonNullByDefault</code> or the <code>@javax.annotation.ParametersAreNonnullByDefault</code> annotation, the parameter of the generated <code>equals</code> method will have the appropriate <code>@Nullable</code> annotation; JDK8+ is required when you do this. <span class="since">(since 1.18.12)</span>.
|
||||
</p>
|
||||
</@f.smallPrint>
|
||||
</@f.scaffold>
|
||||
Reference in new issue
Block a user