From 9ad2bd563b001c0742d767fea9ddaaeb60400ec7 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 8 Jan 2020 01:06:35 +0100 Subject: [fixes #788] lombok generated equals method plus a non-null-by-default annotation no longer clash. --- .../eclipse/handlers/EclipseHandlerUtil.java | 14 ++++++++++ .../eclipse/handlers/HandleEqualsAndHashCode.java | 32 ++++++++++++++++++++-- .../javac/handlers/HandleEqualsAndHashCode.java | 25 +++++++++++++++-- .../lombok/javac/handlers/JavacHandlerUtil.java | 13 +++++++++ src/utils/lombok/javac/JavacTreeMaker.java | 8 +++++- 5 files changed, 86 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 948902d5..1099afd5 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -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; diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 1bca4767..46474b07 100755 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -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 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> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List 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 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.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.nil()); + annsOnParamOnMethod = annsOnParamOnMethod.prepend(m); + } + JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.nil()); List annsOnMethod = List.of(overrideAnnotation); CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode); @@ -388,7 +402,14 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandlernil())); } 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()); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index b3a5cf90..9359b1ae 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -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) { diff --git a/src/utils/lombok/javac/JavacTreeMaker.java b/src/utils/lombok/javac/JavacTreeMaker.java index 84293f11..20f4b66d 100644 --- a/src/utils/lombok/javac/JavacTreeMaker.java +++ b/src/utils/lombok/javac/JavacTreeMaker.java @@ -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 AnnotatedType = MethodId("AnnotatedType", JCExpression.class, List.class, JCExpression.class); + public JCExpression AnnotatedType(List annotations, JCExpression underlyingType) { + return invoke(AnnotatedType, annotations, underlyingType); + } + //javac versions: 6-8 private static final MethodId Call = MethodId("Call"); public JCStatement Call(JCExpression apply) { -- cgit