diff options
25 files changed, 344 insertions, 35 deletions
@@ -400,6 +400,7 @@ lombok.launch.AnnotationProcessorHider$ClaimingProcessor,isolating</echo> <srcdir dir="test/core/src" test="true" /> <srcdir dir="test/bytecode/src" test="true" /> <srcdir dir="test/configuration/src" test="true" /> + <srcdir dir="test/stubs" test="true" /> </module> <settings> <url url="https://projectlombok.org/downloads/lombok.intellij.settings" /> @@ -432,6 +433,7 @@ lombok.launch.AnnotationProcessorHider$ClaimingProcessor,isolating</echo> <srcdir dir="test/core/src" /> <srcdir dir="test/bytecode/src" /> <srcdir dir="test/configuration/src" /> + <srcdir dir="test/stubs" /> <conf name="${eclipse.build.configname}" sources="contrib" /> <conf name="test" sources="contrib" /> <local org="org.projectlombok" name="lombok.patcher" dir="../lombok.patcher" /> @@ -551,6 +553,10 @@ ${sourceWarning}</echo> <src path="test/bytecode/src" /> <src path="test/configuration/src" /> </ivy:compile> + <mkdir dir="build/teststubs" /> + <ivy:compile destdir="build/teststubs" source="1.6" target="1.6" ecj="true" nowarn="true"> + <src path="test/stubs" /> + </ivy:compile> </target> <target name="test-ecj" depends="dist, contrib, setupJavaOracle8TestEnvironment" unless="tests.skip"> @@ -728,6 +734,7 @@ You can also create your own by writing a 'testenvironment.properties' file. The <classpath path="${test.location.javac}" /> <classpath path="build/lombok" /> <classpath path="build/tests" /> + <classpath path="build/teststubs" /> <batchtest> <fileset dir="test/core/src"> <include name="lombok/RunAllTests.java" /> @@ -747,6 +754,7 @@ You can also create your own by writing a 'testenvironment.properties' file. The <classpath path="${test.location.javac}" /> <classpath path="build/lombok" /> <classpath path="build/tests" /> + <classpath path="build/teststubs" /> <batchtest> <fileset dir="test/core/src"> <include name="lombok/RunAllTests.java" /> diff --git a/doc/changelog.markdown b/doc/changelog.markdown index acaa74de..561db859 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -1,11 +1,12 @@ Lombok Changelog ---------------- -### v1.18.7 "Edgy Guinea Pig" +### v1.18.8 (May 7th, 2019) * FEATURE: You can now configure `@FieldNameConstants` to `CONSTANT_CASE` the generated constants, using a `lombok.config` option. See the [FieldNameConstants documentation](https://projectlombok.org/features/experimental/FieldNameConstants). [Issue #2092](https://github.com/rzwitserloot/lombok/issues/2092). * FEATURE: You can now suppress generation of the `builder` method when using `@Builder`; usually because you're only interested in the `toBuilder` method. As a convenience we won't emit warnings about missing `@Builder.Default` annotations when you do this. [Issue #2046](https://github.com/rzwitserloot/lombok/issues/2046) * FEATURE: You can now change the access modifier of generated builder classes. [Issue #2083](https://github.com/rzwitserloot/lombok/issues/2083). * FEATURE: When using `@NonNull`, or any other annotation that would result in a null-check, you can configure to generate an assert statement instead. [Issue #2078](https://github.com/rzwitserloot/lombok/issues/2078). +* FEATURE: Lombok now knows exactly how to treat `@com.fasterxml.jackson.annotation.JsonProperty` and will copy it to the right places for example when making builders. [Issue #1961](https://github.com/rzwitserloot/lombok/issues/1961) [Issue #1981](https://github.com/rzwitserloot/lombok/issues/1981). * PLATFORM: A few lombok features (most notably delombok) failed on JDK12. [Issue #2082](https://github.com/rzwitserloot/lombok/issues/2082) * BUGFIX: var/val on methods that return an intersection type would now work in Eclipse. [Issue #1986](https://github.com/rzwitserloot/lombok/issues/1986) * BUGFIX: Fix for java6 regression if a field has javadoc. [Issue #2066](https://github.com/rzwitserloot/lombok/issues/2066) @@ -13,6 +14,7 @@ Lombok Changelog * BUGFIX: If you use `@Builder` and manually write the `build()` method in your builder class, javac would error out instead of deferring to your implementation. [Issue #2050](https://github.com/rzwitserloot/lombok/issues/2050) [Issue #2061](https://github.com/rzwitserloot/lombok/issues/2061) * BUGFIX: `@SuperBuilder` together with `@Singular` on non-lists would produce an erroneous `emptyList` call. [Issue #2104](https://github.com/rzwitserloot/lombok/issues/2104). * IMPROBABLE BREAKING CHANGE: For fields and parameters marked non-null, if the method body starts with an assert statement to ensure the value isn't null, no code to throw an exception will be generated. +* IMPROBABLE BREAKING CHANGE: When using `ecj` to compile java code with `@Builder` or `@SuperBuilder` in it, and a builder setter method was generated for a `@NonNull`-marked method, no explicit null check would be present. However, running `javac` on the exact same file _would_ produce the null check. Now ecj also produces this null check. [Issue #2120](https://github.com/rzwitserloot/lombok/issues/2120). ### v1.18.6 (February 12th, 2019) * FEATURE: Javadoc on fields will now also be copied to the Builders' setters. Thanks for the contribution, Emil Lundberg. [Issue #2008](https://github.com/rzwitserloot/lombok/issues/2008) diff --git a/src/core/lombok/core/AgentLauncher.java b/src/core/lombok/core/AgentLauncher.java index 1d5ab3e6..5b5af7ba 100644 --- a/src/core/lombok/core/AgentLauncher.java +++ b/src/core/lombok/core/AgentLauncher.java @@ -22,6 +22,7 @@ package lombok.core; import java.lang.instrument.Instrumentation; +import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -35,9 +36,10 @@ public class AgentLauncher { for (AgentInfo info : AGENTS) { try { Class<?> agentClass = Class.forName(info.className()); - AgentLaunchable agent = (AgentLaunchable) agentClass.newInstance(); + AgentLaunchable agent = (AgentLaunchable) agentClass.getConstructor().newInstance(); agent.runAgent(agentArgs, instrumentation, injected, launchingContext); } catch (Throwable t) { + if (t instanceof InvocationTargetException) t = t.getCause(); info.problem(t, instrumentation); } } diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java index 363952a4..46e3b1af 100644 --- a/src/core/lombok/core/AnnotationProcessor.java +++ b/src/core/lombok/core/AnnotationProcessor.java @@ -112,7 +112,7 @@ public class AnnotationProcessor extends AbstractProcessor { try { ClassLoader classLoader = findAndPatchClassLoader(javacProcEnv); - processor = (Processor) Class.forName("lombok.javac.apt.LombokProcessor", false, classLoader).newInstance(); + processor = (Processor) Class.forName("lombok.javac.apt.LombokProcessor", false, classLoader).getConstructor().newInstance(); } catch (Exception e) { delayedWarnings.add("You found a bug in lombok; lombok.javac.apt.LombokProcessor is not available. Lombok will not run during this compilation: " + trace(e)); return false; diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index c300ec39..e3d7368e 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -30,9 +30,9 @@ public class Version { // ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries). // Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example. // Official builds always end in an even number. (Since 0.10.2). - private static final String VERSION = "1.18.7"; - private static final String RELEASE_NAME = "Edgy Guinea Pig"; -// private static final String RELEASE_NAME = "Envious Ferret"; + private static final String VERSION = "1.18.8"; +// private static final String RELEASE_NAME = "Edgy Guinea Pig"; + private static final String RELEASE_NAME = "Envious Ferret"; // Named version history: // Angry Butterfly diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 64d17cd9..6e28f65d 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -76,7 +76,7 @@ public class HandlerUtil { return 43; } - public static final List<String> NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS; + public static final List<String> NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, COPY_TO_SETTER_ANNOTATIONS; static { NONNULL_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { "android.annotation.NonNull", @@ -92,7 +92,7 @@ public class HandlerUtil { "org.jetbrains.annotations.NotNull", "org.jmlspecs.annotation.NonNull", "org.netbeans.api.annotations.common.NonNull", - "org.springframework.lang.NonNull" + "org.springframework.lang.NonNull", })); BASE_COPYABLE_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { "android.support.annotation.NonNull", @@ -298,8 +298,11 @@ public class HandlerUtil { "org.jetbrains.annotations.NotNull", "org.jetbrains.annotations.Nullable", "org.springframework.lang.NonNull", - "org.springframework.lang.Nullable" + "org.springframework.lang.Nullable", })); + COPY_TO_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] { + "com.fasterxml.jackson.annotation.JsonProperty", + })); } /** Checks if the given name is a valid identifier. diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 438314ca..1e791341 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -649,6 +649,15 @@ public class EclipseHandlerUtil { return result == null ? null : result.toArray(new Annotation[0]); } + public static Annotation[] mergeAnnotations(Annotation[] a, Annotation[] b) { + if (a == null || a.length == 0) return (b == null || b.length == 0) ? null : b; + if (b == null || b.length == 0) return a.length == 0 ? null : a; + Annotation[] c = new Annotation[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + public static boolean hasAnnotation(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) { if (node == null) return false; if (type == null) return false; @@ -725,6 +734,17 @@ public class EclipseHandlerUtil { return false; } + public static boolean hasNonNullAnnotations(EclipseNode node, List<Annotation> anns) { + if (anns == null) return false; + for (Annotation annotation : anns) { + TypeReference typeRef = annotation.type; + if (typeRef != null && typeRef.getTypeName() != null) { + for (String bn : NONNULL_ANNOTATIONS) if (typeMatches(bn, node, typeRef)) return true; + } + } + return false; + } + private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0]; /** @@ -755,6 +775,26 @@ public class EclipseHandlerUtil { } /** + * Searches the given field node for annotations that are specifically intentioned to be copied to the setter. + */ + public static Annotation[] findCopyableToSetterAnnotations(EclipseNode node) { + AbstractVariableDeclaration avd = (AbstractVariableDeclaration) node.get(); + if (avd.annotations == null) return EMPTY_ANNOTATIONS_ARRAY; + List<Annotation> result = new ArrayList<Annotation>(); + + for (Annotation annotation : avd.annotations) { + TypeReference typeRef = annotation.type; + if (typeRef != null && typeRef.getTypeName() != null) { + for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, typeRef)) { + result.add(annotation); + break; + } + } + } + return result.toArray(EMPTY_ANNOTATIONS_ARRAY); + } + + /** * Checks if the provided annotation type is likely to be the intended type for the given annotation node. * * This is a guess, but a decent one. diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 4659e9a3..96431a83 100755 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -476,7 +476,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } for (BuilderFieldData bfd : builderFields) { - makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain, accessForInners); + makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain, accessForInners, bfd.originalFieldNode); } { @@ -802,16 +802,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { private static final AbstractMethodDeclaration[] EMPTY = {}; - public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access) { + public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain, AccessLevel access, EclipseNode originalFieldNode) { boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access); + makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain, bfd.annotations, access, originalFieldNode); } else { bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain, access); } } - private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access) { + private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain, Annotation[] annotations, AccessLevel access, EclipseNode originalFieldNode) { TypeDeclaration td = (TypeDeclaration) builderType.get(); AbstractMethodDeclaration[] existing = td.methods; if (existing == null) existing = EMPTY; @@ -827,8 +827,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); + List<Annotation> methodAnnsList = Collections.<Annotation>emptyList(); + Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); + if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns); MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, chain, toEclipseModifier(access), - sourceNode, Collections.<Annotation>emptyList(), annotations != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annotations)) : Collections.<Annotation>emptyList()); + sourceNode, methodAnnsList, annotations != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annotations)) : Collections.<Annotation>emptyList()); injectMethod(builderType, setter); } diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index 529a7d19..4ea83cf6 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -220,7 +220,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { if (isFieldDeprecated(fieldNode) || deprecate) { deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; } - method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated); + method.annotations = mergeAnnotations(copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated), findCopyableToSetterAnnotations(fieldNode)); Argument param = new Argument(field.name, p, copyType(field.type, source), Modifier.FINAL); param.sourceStart = pS; param.sourceEnd = pE; method.arguments = new Argument[] { param }; @@ -238,7 +238,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { Annotation[] copyableAnnotations = findCopyableAnnotations(fieldNode); List<Statement> statements = new ArrayList<Statement>(5); - if (!hasNonNullAnnotations(fieldNode)) { + if (!hasNonNullAnnotations(fieldNode) && !hasNonNullAnnotations(fieldNode, onParam)) { statements.add(assignment); } else { Statement nullCheck = generateNullCheck(field, sourceNode); diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java index 3a387688..9a86d372 100755 --- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java @@ -886,13 +886,13 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { }; if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations); + generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode, bfd.annotations, bfd.originalFieldNode); } else { bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); } } - private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode, Annotation[] annosOnParam) { + private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode, Annotation[] annosOnParam, EclipseNode originalFieldNode) { TypeDeclaration td = (TypeDeclaration) builderType.get(); AbstractMethodDeclaration[] existing = td.methods; if (existing == null) existing = EMPTY_METHODS; @@ -908,8 +908,11 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> { String setterName = fieldNode.getName(); + List<Annotation> methodAnnsList = Collections.<Annotation>emptyList(); + Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); + if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns); MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic, - sourceNode, Collections.<Annotation>emptyList(), annosOnParam != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annosOnParam)) : Collections.<Annotation>emptyList()); + sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(sourceNode.get(), annosOnParam)) : Collections.<Annotation>emptyList()); injectMethod(builderType, setter); } diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 2eaba0fb..d758f602 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -750,10 +750,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JavacTreeMaker maker = fieldNode.getTreeMaker(); - JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.<JCAnnotation>nil(), annosOnParam); + List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); + JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(access), deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, methodAnns, annosOnParam); recursiveSetGeneratedBy(newMethod, source.get(), builderType.getContext()); copyJavadoc(originalFieldNode, newMethod, CopyJavadoc.SETTER); - + injectMethod(builderType, newMethod); } diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index 28f5318d..5482cccc 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2019 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 @@ -234,7 +234,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, field.getContext()); JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(flags, annsOnParam), fieldDecl.name, fieldDecl.vartype, null); - if (!hasNonNullAnnotations(field)) { + if (!hasNonNullAnnotations(field) && !hasNonNullAnnotations(field, onParam)) { statements.append(treeMaker.Exec(assign)); } else { JCStatement nullCheck = generateNullCheck(treeMaker, field, source); @@ -261,7 +261,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { List<JCExpression> throwsClauses = List.nil(); JCExpression annotationMethodDefaultValue = null; - List<JCAnnotation> annsOnMethod = copyAnnotations(onMethod); + List<JCAnnotation> annsOnMethod = mergeAnnotations(copyAnnotations(onMethod), findCopyableToSetterAnnotations(field)); if (isFieldDeprecated(field) || deprecate) { annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil())); } diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java index 290ef89f..0fcaa5f8 100644 --- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java +++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java @@ -861,13 +861,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { }}; if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations); + generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations, fieldNode.originalFieldNode); } else { fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker, AccessLevel.PUBLIC); } } - private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam) { + private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam, JavacNode originalFieldNode) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; for (JavacNode child : builderType.down()) { @@ -881,7 +881,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> { JavacTreeMaker maker = fieldNode.getTreeMaker(); - JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.<JCAnnotation>nil(), annosOnParam); + List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode); + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, methodAnns, annosOnParam); injectMethod(builderType, newMethod); } diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 509a7397..ee012d2b 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1418,6 +1418,15 @@ public class JavacHandlerUtil { return false; } + public static boolean hasNonNullAnnotations(JavacNode node, List<JCAnnotation> anns) { + if (anns == null) return false; + for (JCAnnotation ann : anns) { + for (String nn : NONNULL_ANNOTATIONS) if (typeMatches(nn, node, ann)) return true; + } + + return false; + } + /** * Searches the given field node for annotations and returns each one that is 'copyable' (either via configuration or from the base list). */ @@ -1465,6 +1474,44 @@ public class JavacHandlerUtil { } /** + * Searches the given field node for annotations that are specifically intentioned to be copied to the setter. + */ + public static List<JCAnnotation> findCopyableToSetterAnnotations(JavacNode node) { + JCAnnotation anno = null; + String annoName = null; + for (JavacNode child : node.down()) { + if (child.getKind() == Kind.ANNOTATION) { + if (anno != null) { + annoName = ""; + break; + } + JCAnnotation annotation = (JCAnnotation) child.get(); + annoName = annotation.annotationType.toString(); + anno = annotation; + } + } + + if (annoName == null) return List.nil(); + + if (!annoName.isEmpty()) { + for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, anno.annotationType)) return List.of(anno); + } + + ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>(); + for (JavacNode child : node.down()) { + if (child.getKind() == Kind.ANNOTATION) { + JCAnnotation annotation = (JCAnnotation) child.get(); + boolean match = false; + if (!match) for (String bn : COPY_TO_SETTER_ANNOTATIONS) if (typeMatches(bn, node, annotation.annotationType)) { + result.append(annotation); + break; + } + } + } + return result.toList(); + } + + /** * Generates a new statement that checks if the given variable is null, and if so, throws a configured exception with the * variable name as message. */ @@ -1704,6 +1751,15 @@ public class JavacHandlerUtil { return out.toList(); } + static List<JCAnnotation> mergeAnnotations(List<JCAnnotation> a, List<JCAnnotation> b) { + if (a == null || a.isEmpty()) return b; + if (b == null || b.isEmpty()) return a; + ListBuffer<JCAnnotation> out = new ListBuffer<JCAnnotation>(); + for (JCAnnotation ann : a) out.append(ann); + for (JCAnnotation ann : b) out.append(ann); + return out.toList(); + } + static boolean isClass(JavacNode typeNode) { return isClassAndDoesNotHaveFlags(typeNode, Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION); } diff --git a/src/delombok/lombok/delombok/ant/DelombokTask.java b/src/delombok/lombok/delombok/ant/DelombokTask.java index cb31ef4d..defd1709 100644 --- a/src/delombok/lombok/delombok/ant/DelombokTask.java +++ b/src/delombok/lombok/delombok/ant/DelombokTask.java @@ -188,7 +188,7 @@ class Tasks { Location loc = getLocation(); try { - Object instance = shadowLoadClass("lombok.delombok.ant.DelombokTaskImpl").newInstance(); + Object instance = shadowLoadClass("lombok.delombok.ant.DelombokTaskImpl").getConstructor().newInstance(); for (Field selfField : getClass().getDeclaredFields()) { if (selfField.isSynthetic() || Modifier.isStatic(selfField.getModifiers())) continue; Field otherField = instance.getClass().getDeclaredField(selfField.getName()); @@ -208,7 +208,7 @@ class Tasks { Method m = instance.getClass().getMethod("execute", Location.class); m.invoke(instance, loc); } catch (Exception e) { - Throwable t = (e instanceof InvocationTargetException) ? ((InvocationTargetException) e).getCause() : e; + Throwable t = (e instanceof InvocationTargetException) ? e.getCause() : e; if (t instanceof Error) throw (Error) t; if (t instanceof RuntimeException) throw (RuntimeException) t; throw new RuntimeException(t); diff --git a/src/utils/lombok/core/SpiLoadUtil.java b/src/utils/lombok/core/SpiLoadUtil.java index 02c02496..e685acd6 100644 --- a/src/utils/lombok/core/SpiLoadUtil.java +++ b/src/utils/lombok/core/SpiLoadUtil.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URL; @@ -108,10 +109,13 @@ public class SpiLoadUtil { @Override public C next() { try { - return target.cast(Class.forName(names.next(), true, fLoader).newInstance()); + return target.cast(Class.forName(names.next(), true, fLoader).getConstructor().newInstance()); } catch (Exception e) { - if (e instanceof RuntimeException) throw (RuntimeException)e; - throw new RuntimeException(e); + Throwable t = e; + if (t instanceof InvocationTargetException) t = t.getCause(); + if (t instanceof RuntimeException) throw (RuntimeException) t; + if (t instanceof Error) throw (Error) t; + throw new RuntimeException(t); } } diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index 7a264e39..3cc72f4e 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -409,10 +409,14 @@ public class Javac { } else { try { if (CTC_VOID.equals(tag)) { - return (Type) JC_VOID_TYPE.newInstance(); + return (Type) JC_VOID_TYPE.getConstructor().newInstance(); } else { - return (Type) JC_NO_TYPE.newInstance(); + return (Type) JC_NO_TYPE.getConstructor().newInstance(); } + } catch (InvocationTargetException e) { + throw sneakyThrow(e.getCause()); + } catch (NoSuchMethodException e) { + throw sneakyThrow(e); } catch (IllegalAccessException e) { throw sneakyThrow(e); } catch (InstantiationException e) { diff --git a/test/stubs/com/fasterxml/jackson/annotation/JsonProperty.java b/test/stubs/com/fasterxml/jackson/annotation/JsonProperty.java new file mode 100644 index 00000000..bba58a99 --- /dev/null +++ b/test/stubs/com/fasterxml/jackson/annotation/JsonProperty.java @@ -0,0 +1,12 @@ +package com.fasterxml.jackson.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface JsonProperty { + String value(); +} diff --git a/test/transform/resource/after-delombok/BuilderWithNonNull.java b/test/transform/resource/after-delombok/BuilderWithNonNull.java new file mode 100644 index 00000000..bee7d415 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderWithNonNull.java @@ -0,0 +1,40 @@ +class BuilderWithNonNull { + @lombok.NonNull + private final String id; + @java.lang.SuppressWarnings("all") + BuilderWithNonNull(@lombok.NonNull final String id) { + if (id == null) { + throw new java.lang.NullPointerException("id is marked non-null but is null"); + } + this.id = id; + } + @java.lang.SuppressWarnings("all") + public static class BuilderWithNonNullBuilder { + @java.lang.SuppressWarnings("all") + private String id; + @java.lang.SuppressWarnings("all") + BuilderWithNonNullBuilder() { + } + @java.lang.SuppressWarnings("all") + public BuilderWithNonNullBuilder id(@lombok.NonNull final String id) { + if (id == null) { + throw new java.lang.NullPointerException("id is marked non-null but is null"); + } + this.id = id; + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderWithNonNull build() { + return new BuilderWithNonNull(id); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "BuilderWithNonNull.BuilderWithNonNullBuilder(id=" + this.id + ")"; + } + } + @java.lang.SuppressWarnings("all") + public static BuilderWithNonNullBuilder builder() { + return new BuilderWithNonNullBuilder(); + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-delombok/JacksonJsonProperty.java b/test/transform/resource/after-delombok/JacksonJsonProperty.java new file mode 100644 index 00000000..842ba298 --- /dev/null +++ b/test/transform/resource/after-delombok/JacksonJsonProperty.java @@ -0,0 +1,41 @@ +import com.fasterxml.jackson.annotation.JsonProperty; +public class JacksonJsonProperty { + @JsonProperty("kebab-case-prop") + public String kebabCaseProp; + @java.lang.SuppressWarnings("all") + JacksonJsonProperty(final String kebabCaseProp) { + this.kebabCaseProp = kebabCaseProp; + } + @java.lang.SuppressWarnings("all") + public static class JacksonJsonPropertyBuilder { + @java.lang.SuppressWarnings("all") + private String kebabCaseProp; + @java.lang.SuppressWarnings("all") + JacksonJsonPropertyBuilder() { + } + @JsonProperty("kebab-case-prop") + @java.lang.SuppressWarnings("all") + public JacksonJsonPropertyBuilder kebabCaseProp(final String kebabCaseProp) { + this.kebabCaseProp = kebabCaseProp; + return this; + } + @java.lang.SuppressWarnings("all") + public JacksonJsonProperty build() { + return new JacksonJsonProperty(kebabCaseProp); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "JacksonJsonProperty.JacksonJsonPropertyBuilder(kebabCaseProp=" + this.kebabCaseProp + ")"; + } + } + @java.lang.SuppressWarnings("all") + public static JacksonJsonPropertyBuilder builder() { + return new JacksonJsonPropertyBuilder(); + } + @JsonProperty("kebab-case-prop") + @java.lang.SuppressWarnings("all") + public void setKebabCaseProp(final String kebabCaseProp) { + this.kebabCaseProp = kebabCaseProp; + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/BuilderWithNonNull.java b/test/transform/resource/after-ecj/BuilderWithNonNull.java new file mode 100644 index 00000000..a8ef93f0 --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderWithNonNull.java @@ -0,0 +1,34 @@ +@lombok.Builder class BuilderWithNonNull { + public static @java.lang.SuppressWarnings("all") class BuilderWithNonNullBuilder { + private @java.lang.SuppressWarnings("all") String id; + @java.lang.SuppressWarnings("all") BuilderWithNonNullBuilder() { + super(); + } + public @java.lang.SuppressWarnings("all") BuilderWithNonNullBuilder id(final @lombok.NonNull String id) { + if ((id == null)) + { + throw new java.lang.NullPointerException("id is marked non-null but is null"); + } + this.id = id; + return this; + } + public @java.lang.SuppressWarnings("all") BuilderWithNonNull build() { + return new BuilderWithNonNull(id); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("BuilderWithNonNull.BuilderWithNonNullBuilder(id=" + this.id) + ")"); + } + } + private final @lombok.NonNull String id; + @java.lang.SuppressWarnings("all") BuilderWithNonNull(final @lombok.NonNull String id) { + super(); + if ((id == null)) + { + throw new java.lang.NullPointerException("id is marked non-null but is null"); + } + this.id = id; + } + public static @java.lang.SuppressWarnings("all") BuilderWithNonNullBuilder builder() { + return new BuilderWithNonNullBuilder(); + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/JacksonJsonProperty.java b/test/transform/resource/after-ecj/JacksonJsonProperty.java new file mode 100644 index 00000000..73049b2a --- /dev/null +++ b/test/transform/resource/after-ecj/JacksonJsonProperty.java @@ -0,0 +1,32 @@ +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Setter; +public @Builder class JacksonJsonProperty { + public static @java.lang.SuppressWarnings("all") class JacksonJsonPropertyBuilder { + private @java.lang.SuppressWarnings("all") String kebabCaseProp; + @java.lang.SuppressWarnings("all") JacksonJsonPropertyBuilder() { + super(); + } + public @JsonProperty("kebab-case-prop") @java.lang.SuppressWarnings("all") JacksonJsonPropertyBuilder kebabCaseProp(final String kebabCaseProp) { + this.kebabCaseProp = kebabCaseProp; + return this; + } + public @java.lang.SuppressWarnings("all") JacksonJsonProperty build() { + return new JacksonJsonProperty(kebabCaseProp); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("JacksonJsonProperty.JacksonJsonPropertyBuilder(kebabCaseProp=" + this.kebabCaseProp) + ")"); + } + } + public @JsonProperty("kebab-case-prop") @Setter String kebabCaseProp; + @java.lang.SuppressWarnings("all") JacksonJsonProperty(final String kebabCaseProp) { + super(); + this.kebabCaseProp = kebabCaseProp; + } + public static @java.lang.SuppressWarnings("all") JacksonJsonPropertyBuilder builder() { + return new JacksonJsonPropertyBuilder(); + } + public @JsonProperty("kebab-case-prop") @java.lang.SuppressWarnings("all") void setKebabCaseProp(final String kebabCaseProp) { + this.kebabCaseProp = kebabCaseProp; + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/SuperBuilderWithNonNull.java b/test/transform/resource/after-ecj/SuperBuilderWithNonNull.java index 4b5cb188..616d7083 100644 --- a/test/transform/resource/after-ecj/SuperBuilderWithNonNull.java +++ b/test/transform/resource/after-ecj/SuperBuilderWithNonNull.java @@ -10,6 +10,10 @@ public class SuperBuilderWithNonNull { protected abstract @java.lang.SuppressWarnings("all") B self();
public abstract @java.lang.SuppressWarnings("all") C build();
public @java.lang.SuppressWarnings("all") B nonNullParentField(final @lombok.NonNull String nonNullParentField) {
+ if ((nonNullParentField == null))
+ {
+ throw new java.lang.NullPointerException("nonNullParentField is marked non-null but is null");
+ }
this.nonNullParentField = nonNullParentField;
nonNullParentField$set = true;
return self();
@@ -57,6 +61,10 @@ public class SuperBuilderWithNonNull { protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
public @java.lang.SuppressWarnings("all") B nonNullChildField(final @lombok.NonNull String nonNullChildField) {
+ if ((nonNullChildField == null))
+ {
+ throw new java.lang.NullPointerException("nonNullChildField is marked non-null but is null");
+ }
this.nonNullChildField = nonNullChildField;
return self();
}
diff --git a/test/transform/resource/before/BuilderWithNonNull.java b/test/transform/resource/before/BuilderWithNonNull.java new file mode 100644 index 00000000..03a54326 --- /dev/null +++ b/test/transform/resource/before/BuilderWithNonNull.java @@ -0,0 +1,5 @@ +@lombok.Builder +class BuilderWithNonNull { + @lombok.NonNull + private final String id; +} diff --git a/test/transform/resource/before/JacksonJsonProperty.java b/test/transform/resource/before/JacksonJsonProperty.java new file mode 100644 index 00000000..f002dc48 --- /dev/null +++ b/test/transform/resource/before/JacksonJsonProperty.java @@ -0,0 +1,10 @@ +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Setter; + +@Builder +public class JacksonJsonProperty { + @JsonProperty("kebab-case-prop") + @Setter + public String kebabCaseProp; +} |