From 56150952c451f0d8c2018424191d4480ac5e8460 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Mon, 11 Mar 2013 22:04:27 +0100
Subject: Added @Log4j2 support.
---
test/transform/resource/before/LoggerLog4j2.java | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 test/transform/resource/before/LoggerLog4j2.java
(limited to 'test/transform/resource/before')
diff --git a/test/transform/resource/before/LoggerLog4j2.java b/test/transform/resource/before/LoggerLog4j2.java
new file mode 100644
index 00000000..b7ea99ee
--- /dev/null
+++ b/test/transform/resource/before/LoggerLog4j2.java
@@ -0,0 +1,9 @@
+import lombok.extern.log4j.Log4j2;
+
+@lombok.extern.log4j.Log4j2
+class LoggerLog4j2 {
+}
+
+@Log4j2
+class LoggerLog4j2WithImport {
+}
\ No newline at end of file
--
cgit
From f98bf919cc6701e98087d39fefb7bbfc85688834 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Mon, 6 May 2013 22:09:13 +0200
Subject: Fixed issue 513: If equals is present but hashCode isn't, @Data now
generates a warning to explain this strange situation.
---
doc/changelog.markdown | 3 +
.../eclipse/handlers/HandleEqualsAndHashCode.java | 23 ++++--
.../javac/handlers/HandleEqualsAndHashCode.java | 22 ++++--
.../EqualsAndHashCodeWithSomeExistingMethods.java | 82 ++++++++++++++++++++++
.../EqualsAndHashCodeWithSomeExistingMethods.java | 71 +++++++++++++++++++
.../EqualsAndHashCodeWithSomeExistingMethods.java | 47 +++++++++++++
...ndHashCodeWithSomeExistingMethods.java.messages | 2 +
...ndHashCodeWithSomeExistingMethods.java.messages | 2 +
8 files changed, 239 insertions(+), 13 deletions(-)
create mode 100644 test/transform/resource/after-delombok/EqualsAndHashCodeWithSomeExistingMethods.java
create mode 100644 test/transform/resource/after-ecj/EqualsAndHashCodeWithSomeExistingMethods.java
create mode 100644 test/transform/resource/before/EqualsAndHashCodeWithSomeExistingMethods.java
create mode 100644 test/transform/resource/messages-delombok/EqualsAndHashCodeWithSomeExistingMethods.java.messages
create mode 100644 test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages
(limited to 'test/transform/resource/before')
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index 15e3c1e0..32af72d3 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -1,6 +1,9 @@
Lombok Changelog
----------------
+### v0.11.9 (Edgy Guinea Pig)
+* BUGFIX: When using `@Data`, warnings are not generated if certain aspects are not generated because you wrote explicit versions of them. However, this gets confusing with `equals` / `hashCode` / `canEqual`, as nothing is generated if any one of those methods is present. Now, if one of `equals` or `hashCode` is present but not the other one (or `canEqual` is present but `equals` and/or `hashCode` is missing), a warning is emitted to explain that lombok will not generate any of the equals / hashCode methods, and that you should either write them all yourself or remove them all. [Issue #513](https://code.google.com/p/projectlombok/issues/detail?id=513)
+
### v0.11.8 (April 23rd, 2013)
* FEATURE: Major performance improvements in eclipse by profiling the project clean process.
* CHANGE: {Experimental} The experimental `@Value` feature no longer implies the also experimental `@Wither`. If you like your `@Value` classes to make withers, add `@Wither` to the class right next to `@Value`.
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 0c82b74c..6990e609 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 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
@@ -204,18 +204,27 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler existsResults = new ArrayList();
- existsResults.add(methodExists("equals", typeNode, 1));
- existsResults.add(methodExists("hashCode", typeNode, 0));
- existsResults.add(methodExists("canEqual", typeNode, 1));
- switch (Collections.max(existsResults)) {
+ boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
+ MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
+ MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
+ MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
+ switch (Collections.max(Arrays.asList(equalsExists, hashCodeExists, canEqualExists))) {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
errorNode.addWarning(msg);
+ } else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
+ // This means equals OR hashCode exists and not both (or neither, but canEqual is there).
+ // Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
+ // The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 3 methods are
+ // all inter-related and should be written by the same entity.
+ String msg = String.format("Not generating %s: One of equals, hashCode, and canEqual exists. " +
+ "You should either write all of these are none of these (in the latter case, lombok generates them).",
+ equalsExists == MemberExistsResult.NOT_EXISTS && hashCodeExists == MemberExistsResult.NOT_EXISTS ? "equals and hashCode" :
+ equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
+ errorNode.addWarning(msg);
}
return;
case NOT_EXISTS:
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 5f69be9d..3b1e226f 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 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
@@ -25,6 +25,7 @@ import static lombok.javac.Javac.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import lombok.EqualsAndHashCode;
@@ -178,17 +179,26 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler existsResults = new ArrayList();
- existsResults.add(methodExists("equals", typeNode, 1));
- existsResults.add(methodExists("hashCode", typeNode, 0));
- existsResults.add(methodExists("canEqual", typeNode, 1));
- switch (Collections.max(existsResults)) {
+ MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
+ MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
+ MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
+ switch (Collections.max(Arrays.asList(equalsExists, hashCodeExists, canEqualExists))) {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
source.addWarning(msg);
+ } else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
+ // This means equals OR hashCode exists and not both (or neither, but canEqual is there).
+ // Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
+ // The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 3 methods are
+ // all inter-related and should be written by the same entity.
+ String msg = String.format("Not generating %s: One of equals, hashCode, and canEqual exists. " +
+ "You should either write all of these are none of these (in the latter case, lombok generates them).",
+ equalsExists == MemberExistsResult.NOT_EXISTS && hashCodeExists == MemberExistsResult.NOT_EXISTS ? "equals and hashCode" :
+ equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
+ source.addWarning(msg);
}
return;
case NOT_EXISTS:
diff --git a/test/transform/resource/after-delombok/EqualsAndHashCodeWithSomeExistingMethods.java b/test/transform/resource/after-delombok/EqualsAndHashCodeWithSomeExistingMethods.java
new file mode 100644
index 00000000..0a6b1e7f
--- /dev/null
+++ b/test/transform/resource/after-delombok/EqualsAndHashCodeWithSomeExistingMethods.java
@@ -0,0 +1,82 @@
+import lombok.*;
+import static lombok.AccessLevel.NONE;
+class EqualsAndHashCodeWithSomeExistingMethods {
+ int x;
+ public int hashCode() {
+ return 42;
+ }
+ @java.lang.SuppressWarnings("all")
+ public EqualsAndHashCodeWithSomeExistingMethods() {
+
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "EqualsAndHashCodeWithSomeExistingMethods(x=" + this.x + ")";
+ }
+}
+class EqualsAndHashCodeWithSomeExistingMethods2 {
+ int x;
+ public boolean canEqual(Object other) {
+ return false;
+ }
+ @java.lang.SuppressWarnings("all")
+ public EqualsAndHashCodeWithSomeExistingMethods2() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "EqualsAndHashCodeWithSomeExistingMethods2(x=" + this.x + ")";
+ }
+}
+class EqualsAndHashCodeWithAllExistingMethods {
+ int x;
+ public int hashCode() {
+ return 42;
+ }
+ public boolean equals(Object other) {
+ return false;
+ }
+ @java.lang.SuppressWarnings("all")
+ public EqualsAndHashCodeWithAllExistingMethods() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "EqualsAndHashCodeWithAllExistingMethods(x=" + this.x + ")";
+ }
+}
+class EqualsAndHashCodeWithNoExistingMethods {
+ int x;
+ @java.lang.SuppressWarnings("all")
+ public EqualsAndHashCodeWithNoExistingMethods() {
+
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof EqualsAndHashCodeWithNoExistingMethods)) return false;
+ final EqualsAndHashCodeWithNoExistingMethods other = (EqualsAndHashCodeWithNoExistingMethods)o;
+ if (!other.canEqual((java.lang.Object)this)) return false;
+ if (this.x != other.x) return false;
+ return true;
+ }
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof EqualsAndHashCodeWithNoExistingMethods;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * PRIME + this.x;
+ return result;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "EqualsAndHashCodeWithNoExistingMethods(x=" + this.x + ")";
+ }
+}
diff --git a/test/transform/resource/after-ecj/EqualsAndHashCodeWithSomeExistingMethods.java b/test/transform/resource/after-ecj/EqualsAndHashCodeWithSomeExistingMethods.java
new file mode 100644
index 00000000..cdd771a4
--- /dev/null
+++ b/test/transform/resource/after-ecj/EqualsAndHashCodeWithSomeExistingMethods.java
@@ -0,0 +1,71 @@
+import lombok.*;
+import static lombok.AccessLevel.NONE;
+@Data @Getter(NONE) @Setter(NONE) class EqualsAndHashCodeWithSomeExistingMethods {
+ int x;
+ public int hashCode() {
+ return 42;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("EqualsAndHashCodeWithSomeExistingMethods(x=" + this.x) + ")");
+ }
+ public @java.lang.SuppressWarnings("all") EqualsAndHashCodeWithSomeExistingMethods() {
+ super();
+ }
+}
+@Data @Getter(NONE) @Setter(NONE) class EqualsAndHashCodeWithSomeExistingMethods2 {
+ int x;
+ public boolean canEqual(Object other) {
+ return false;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("EqualsAndHashCodeWithSomeExistingMethods2(x=" + this.x) + ")");
+ }
+ public @java.lang.SuppressWarnings("all") EqualsAndHashCodeWithSomeExistingMethods2() {
+ super();
+ }
+}
+@Data @Getter(NONE) @Setter(NONE) class EqualsAndHashCodeWithAllExistingMethods {
+ int x;
+ public int hashCode() {
+ return 42;
+ }
+ public boolean equals(Object other) {
+ return false;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("EqualsAndHashCodeWithAllExistingMethods(x=" + this.x) + ")");
+ }
+ public @java.lang.SuppressWarnings("all") EqualsAndHashCodeWithAllExistingMethods() {
+ super();
+ }
+}
+@Data @Getter(AccessLevel.NONE) @Setter(lombok.AccessLevel.NONE) class EqualsAndHashCodeWithNoExistingMethods {
+ int x;
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((! (o instanceof EqualsAndHashCodeWithNoExistingMethods)))
+ return false;
+ final @java.lang.SuppressWarnings("all") EqualsAndHashCodeWithNoExistingMethods other = (EqualsAndHashCodeWithNoExistingMethods) o;
+ if ((! other.canEqual((java.lang.Object) this)))
+ return false;
+ if ((this.x != other.x))
+ return false;
+ return true;
+ }
+ public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
+ return (other instanceof EqualsAndHashCodeWithNoExistingMethods);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = ((result * PRIME) + this.x);
+ return result;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("EqualsAndHashCodeWithNoExistingMethods(x=" + this.x) + ")");
+ }
+ public @java.lang.SuppressWarnings("all") EqualsAndHashCodeWithNoExistingMethods() {
+ super();
+ }
+}
diff --git a/test/transform/resource/before/EqualsAndHashCodeWithSomeExistingMethods.java b/test/transform/resource/before/EqualsAndHashCodeWithSomeExistingMethods.java
new file mode 100644
index 00000000..784e3b3f
--- /dev/null
+++ b/test/transform/resource/before/EqualsAndHashCodeWithSomeExistingMethods.java
@@ -0,0 +1,47 @@
+import lombok.*;
+import static lombok.AccessLevel.NONE;
+
+@Data
+@Getter(NONE)
+@Setter(NONE)
+class EqualsAndHashCodeWithSomeExistingMethods {
+ int x;
+
+ public int hashCode() {
+ return 42;
+ }
+}
+
+@Data
+@Getter(NONE)
+@Setter(NONE)
+class EqualsAndHashCodeWithSomeExistingMethods2 {
+ int x;
+
+ public boolean canEqual(Object other) {
+ return false;
+ }
+}
+
+@Data
+@Getter(NONE)
+@Setter(NONE)
+class EqualsAndHashCodeWithAllExistingMethods {
+ int x;
+
+ public int hashCode() {
+ return 42;
+ }
+
+ public boolean equals(Object other) {
+ return false;
+ }
+}
+
+@Data
+@Getter(AccessLevel.NONE)
+@Setter(lombok.AccessLevel.NONE)
+class EqualsAndHashCodeWithNoExistingMethods {
+ int x;
+}
+
diff --git a/test/transform/resource/messages-delombok/EqualsAndHashCodeWithSomeExistingMethods.java.messages b/test/transform/resource/messages-delombok/EqualsAndHashCodeWithSomeExistingMethods.java.messages
new file mode 100644
index 00000000..9a0b29f3
--- /dev/null
+++ b/test/transform/resource/messages-delombok/EqualsAndHashCodeWithSomeExistingMethods.java.messages
@@ -0,0 +1,2 @@
+4:1 Not generating equals: One of equals, hashCode, and canEqual exists. You should either write all of these are none of these (in the latter case, lombok generates them).
+15:1 Not generating equals and hashCode: One of equals, hashCode, and canEqual exists. You should either write all of these are none of these (in the latter case, lombok generates them).
diff --git a/test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages b/test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages
new file mode 100644
index 00000000..cf6ebea2
--- /dev/null
+++ b/test/transform/resource/messages-ecj/EqualsAndHashCodeWithSomeExistingMethods.java.messages
@@ -0,0 +1,2 @@
+4:57 Not generating equals: One of equals, hashCode, and canEqual exists. You should either write all of these are none of these (in the latter case, lombok generates them).
+15:194 Not generating equals and hashCode: One of equals, hashCode, and canEqual exists. You should either write all of these are none of these (in the latter case, lombok generates them).
--
cgit
From b3b91a0ddd1770f8646d27e421e2cfef96940f8f Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Fri, 24 May 2013 01:02:13 +0200
Subject: added test case and delombok result for issue 520 (val in
try-with-resources). WARNING: I havent added ecj test output yet because so
far there is no ecj7 available for testing with our buildscripts. I should
fix that first and then Ill sort this out.
---
.../resource/after-delombok/ValInTryWithResources.java | 10 ++++++++++
test/transform/resource/before/ValInTryWithResources.java | 12 ++++++++++++
2 files changed, 22 insertions(+)
create mode 100644 test/transform/resource/after-delombok/ValInTryWithResources.java
create mode 100644 test/transform/resource/before/ValInTryWithResources.java
(limited to 'test/transform/resource/before')
diff --git a/test/transform/resource/after-delombok/ValInTryWithResources.java b/test/transform/resource/after-delombok/ValInTryWithResources.java
new file mode 100644
index 00000000..73f8d1a2
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValInTryWithResources.java
@@ -0,0 +1,10 @@
+//version 7
+import java.io.IOException;
+public class ValInTryWithResources {
+ public void whyTryInsteadOfCleanup() throws IOException {
+ try (final java.io.InputStream in = getClass().getResourceAsStream("ValInTryWithResources.class");) {
+ final java.io.InputStream i = in;
+ final int j = in.read();
+ }
+ }
+}
diff --git a/test/transform/resource/before/ValInTryWithResources.java b/test/transform/resource/before/ValInTryWithResources.java
new file mode 100644
index 00000000..780e1c71
--- /dev/null
+++ b/test/transform/resource/before/ValInTryWithResources.java
@@ -0,0 +1,12 @@
+//version 7
+import lombok.val;
+import java.io.IOException;
+
+public class ValInTryWithResources {
+ public void whyTryInsteadOfCleanup() throws IOException {
+ try (val in = getClass().getResourceAsStream("ValInTryWithResources.class")) {
+ val i = in;
+ val j = in.read();
+ }
+ }
+}
\ No newline at end of file
--
cgit
From 5a3e9bd8049469169410107011ad0e26b3b629e3 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Fri, 31 May 2013 01:03:38 +0200
Subject: Added @NonNull on parameters feature (issue 514), including docs and
changelog.
---
buildScripts/website.ant.xml | 3 +
doc/changelog.markdown | 1 +
src/core/lombok/NonNull.java | 16 ++-
.../eclipse/handlers/EclipseHandlerUtil.java | 7 +-
.../lombok/eclipse/handlers/NonNullHandler.java | 147 ++++++++++++++++++++
.../lombok/javac/handlers/HandleSneakyThrows.java | 12 +-
.../lombok/javac/handlers/JavacHandlerUtil.java | 23 +++-
src/core/lombok/javac/handlers/NonNullHandler.java | 148 +++++++++++++++++++++
src/utils/lombok/javac/Javac.java | 32 +++++
.../resource/after-delombok/DataOnLocalClass.java | 8 +-
.../after-delombok/NonNullOnParameter.java | 48 +++++++
.../resource/after-delombok/NonNullPlain.java | 8 +-
.../resource/after-delombok/SetterOnClass.java | 4 +-
.../resource/after-delombok/WitherOnClass.java | 4 +-
.../resource/after-ecj/DataOnLocalClass.java | 8 +-
.../resource/after-ecj/NonNullOnParameter.java | 61 +++++++++
.../transform/resource/after-ecj/NonNullPlain.java | 8 +-
.../resource/after-ecj/SetterOnClass.java | 4 +-
.../resource/after-ecj/WitherOnClass.java | 4 +-
.../resource/before/NonNullOnParameter.java | 30 +++++
.../NonNullOnParameter.java.messages | 1 +
.../messages-delombok/NonNullPlain.java.messages | 1 +
.../messages-ecj/NonNullOnParameter.java.messages | 3 +
.../messages-ecj/NonNullPlain.java.messages | 1 +
.../NonNullOnParameter.java.messages | 1 +
.../messages-idempotent/NonNullPlain.java.messages | 3 +
usage_examples/NonNullExample_post.jpage | 13 ++
usage_examples/NonNullExample_pre.jpage | 10 ++
website/features/Cleanup.html | 2 +-
website/features/Data.html | 2 +-
website/features/Delegate.html | 11 +-
website/features/GetterLazy.html | 2 +-
website/features/GetterSetter.html | 2 +-
website/features/Log.html | 2 +-
website/features/NonNull.html | 73 ++++++++++
website/features/SneakyThrows.html | 4 +-
website/features/Synchronized.html | 2 +-
website/features/ToString.html | 2 +-
website/features/index.html | 18 +--
website/features/val.html | 2 +-
40 files changed, 674 insertions(+), 57 deletions(-)
create mode 100644 src/core/lombok/eclipse/handlers/NonNullHandler.java
create mode 100644 src/core/lombok/javac/handlers/NonNullHandler.java
create mode 100644 test/transform/resource/after-delombok/NonNullOnParameter.java
create mode 100644 test/transform/resource/after-ecj/NonNullOnParameter.java
create mode 100644 test/transform/resource/before/NonNullOnParameter.java
create mode 100644 test/transform/resource/messages-delombok/NonNullOnParameter.java.messages
create mode 100644 test/transform/resource/messages-delombok/NonNullPlain.java.messages
create mode 100644 test/transform/resource/messages-ecj/NonNullOnParameter.java.messages
create mode 100644 test/transform/resource/messages-ecj/NonNullPlain.java.messages
create mode 100644 test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages
create mode 100644 test/transform/resource/messages-idempotent/NonNullPlain.java.messages
create mode 100644 usage_examples/NonNullExample_post.jpage
create mode 100644 usage_examples/NonNullExample_pre.jpage
create mode 100644 website/features/NonNull.html
(limited to 'test/transform/resource/before')
diff --git a/buildScripts/website.ant.xml b/buildScripts/website.ant.xml
index 405b388f..41130bd2 100644
--- a/buildScripts/website.ant.xml
+++ b/buildScripts/website.ant.xml
@@ -145,6 +145,9 @@ such as converting the changelog into HTML, and creating javadoc.
+
+
+
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index e2e3d6b5..aaf66030 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -2,6 +2,7 @@ Lombok Changelog
----------------
### v0.11.9 (Edgy Guinea Pig)
+* FEATURE: `@NonNull` on a method or constructor parameter now generates a null-check statement at the start of your method. This nullcheck will throw a `NullPointerException` with the name of the parameter as the message. [Issue #514](https://code.google.com/p/projectlombok/issues/detail?id=514)
* BUGFIX: Usage of `Lombok.sneakyThrow()` or `@SneakyThrows` would sometimes result in invalid classes (classes which fail with `VerifyError`). [Issue #470](https://code.google.com/p/projectlombok/issues/detail?id=470)
* BUGFIX: Using `val` in try-with-resources did not work for javac. [Issue #520](https://code.google.com/p/projectlombok/issues/detail?id=520)
* BUGFIX: When using `@Data`, warnings are not generated if certain aspects are not generated because you wrote explicit versions of them. However, this gets confusing with `equals` / `hashCode` / `canEqual`, as nothing is generated if any one of those methods is present. Now, if one of `equals` or `hashCode` is present but not the other one (or `canEqual` is present but `equals` and/or `hashCode` is missing), a warning is emitted to explain that lombok will not generate any of the equals / hashCode methods, and that you should either write them all yourself or remove them all. [Issue #513](https://code.google.com/p/projectlombok/issues/detail?id=513)
diff --git a/src/core/lombok/NonNull.java b/src/core/lombok/NonNull.java
index 5f5d8ed2..96813170 100644
--- a/src/core/lombok/NonNull.java
+++ b/src/core/lombok/NonNull.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 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
@@ -28,12 +28,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Lombok is smart enough to translate any annotation named {@code @NonNull} in any casing and
- * with any package name to the return type of generated getters and the parameter of generated setters and constructors,
- * as well as generate the appropriate null checks in the setter and constructor.
- *
- * You can use this annotation for the purpose, though you can also use JSR305's annotation, findbugs's, pmd's, or IDEA's, or just
- * about anyone elses. As long as it is named {@code @NonNull}.
+ * If put on a parameter, lombok will insert a null-check at the start of the method / constructor's body, throwing a
+ * {@code NullPointerException} with the parameter's name as message. If put on a field, any generated method assigning
+ * a value to this field will also produce these nullchecks.
+ *
+ * Note that any annotation named {@code NonNull} with any casing and any package will result in nullchecks produced for
+ * generated methods (and the annotation will be copied to the getter return type and any parameters of generated methods),
+ * but only this annotation, if present on a parameter, will result in a null check inserted into your otherwise
+ * handwritten method.
*
* WARNING: If the java community ever does decide on supporting a single {@code @NonNull} annotation (for example via JSR305), then
* this annotation will be deleted from the lombok package. If the need to update an import statement scares
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 7703336f..dc99dabf 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -61,6 +61,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
@@ -1334,7 +1335,11 @@ public class EclipseHandlerUtil {
EqualExpression equalExpression = new EqualExpression(varName, nullLiteral, OperatorIds.EQUAL_EQUAL);
equalExpression.sourceStart = pS; equalExpression.statementEnd = equalExpression.sourceEnd = pE;
setGeneratedBy(equalExpression, source);
- IfStatement ifStatement = new IfStatement(equalExpression, throwStatement, 0, 0);
+ Block throwBlock = new Block(0);
+ throwBlock.statements = new Statement[] {throwStatement};
+ throwBlock.sourceStart = pS; throwBlock.sourceEnd = pE;
+ setGeneratedBy(throwBlock, source);
+ IfStatement ifStatement = new IfStatement(equalExpression, throwBlock, 0, 0);
setGeneratedBy(ifStatement, source);
return ifStatement;
}
diff --git a/src/core/lombok/eclipse/handlers/NonNullHandler.java b/src/core/lombok/eclipse/handlers/NonNullHandler.java
new file mode 100644
index 00000000..5c58069c
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/NonNullHandler.java
@@ -0,0 +1,147 @@
+/*
+ * 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;
+
+ // 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/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java
index c2394fc8..c818f630 100644
--- a/src/core/lombok/javac/handlers/HandleSneakyThrows.java
+++ b/src/core/lombok/javac/handlers/HandleSneakyThrows.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
@@ -36,8 +36,6 @@ import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
-import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -114,14 +112,6 @@ public class HandleSneakyThrows extends JavacAnnotationHandler {
}
}
- private boolean isConstructorCall(final JCStatement supect) {
- if (!(supect instanceof JCExpressionStatement)) return false;
- final JCExpression supectExpression = ((JCExpressionStatement) supect).expr;
- if (!(supectExpression instanceof JCMethodInvocation)) return false;
- final String methodName = ((JCMethodInvocation) supectExpression).meth.toString();
- return "super".equals(methodName) || "this".equals(methodName);
- }
-
private JCStatement buildTryCatchBlock(JavacNode node, List contents, String exception, JCTree source) {
TreeMaker maker = node.getTreeMaker();
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index ef1a9f50..7cbaa5ac 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -50,9 +50,11 @@ import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
@@ -121,6 +123,7 @@ public class JavacHandlerUtil {
}
public static T recursiveSetGeneratedBy(T node, JCTree source) {
+ if (node == null) return null;
setGeneratedBy(node, source);
node.accept(new MarkingScanner(source));
@@ -543,6 +546,23 @@ public class JavacHandlerUtil {
return MemberExistsResult.NOT_EXISTS;
}
+ public static boolean isConstructorCall(final JCStatement statement) {
+ if (!(statement instanceof JCExpressionStatement)) return false;
+ JCExpression expr = ((JCExpressionStatement) statement).expr;
+ if (!(expr instanceof JCMethodInvocation)) return false;
+ JCExpression invocation = ((JCMethodInvocation) expr).meth;
+ String name;
+ if (invocation instanceof JCFieldAccess) {
+ name = ((JCFieldAccess) invocation).name.toString();
+ } else if (invocation instanceof JCIdent) {
+ name = ((JCIdent) invocation).name.toString();
+ } else {
+ name = "";
+ }
+
+ return "super".equals(name) || "this".equals(name);
+ }
+
/**
* Turns an {@code AccessLevel} instance into the flag bit used by javac.
*/
@@ -890,7 +910,8 @@ public class JavacHandlerUtil {
JCExpression npe = chainDots(variable, "java", "lang", "NullPointerException");
JCTree exception = treeMaker.NewClass(null, List.nil(), npe, List.of(treeMaker.Literal(fieldName.toString())), null);
JCStatement throwStatement = treeMaker.Throw(exception);
- return treeMaker.If(treeMaker.Binary(CTC_EQUAL, treeMaker.Ident(fieldName), treeMaker.Literal(CTC_BOT, null)), throwStatement, null);
+ JCBlock throwBlock = treeMaker.Block(0, List.of(throwStatement));
+ return treeMaker.If(treeMaker.Binary(CTC_EQUAL, treeMaker.Ident(fieldName), treeMaker.Literal(CTC_BOT, null)), throwBlock, null);
}
/**
diff --git a/src/core/lombok/javac/handlers/NonNullHandler.java b/src/core/lombok/javac/handlers/NonNullHandler.java
new file mode 100644
index 00000000..415d6032
--- /dev/null
+++ b/src/core/lombok/javac/handlers/NonNullHandler.java
@@ -0,0 +1,148 @@
+/*
+ * 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;
+
+@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;
+
+ // 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 (getTag(bin) != CTC_EQUAL) return null;
+ if (!(bin.lhs instanceof JCIdent)) return null;
+ if (!(bin.rhs instanceof JCLiteral)) return null;
+ if (((JCLiteral) bin.rhs).typetag != CTC_BOT) return null;
+ return ((JCIdent) bin.lhs).name.toString();
+ }
+ }
+}
diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java
index b4e58b8f..08c7c957 100644
--- a/src/utils/lombok/javac/Javac.java
+++ b/src/utils/lombok/javac/Javac.java
@@ -21,6 +21,8 @@
*/
package lombok.javac;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -129,4 +131,34 @@ public class Javac {
throw new RuntimeException(e);
}
}
+
+ private static final Field JCTREE_TAG;
+ private static final Method JCTREE_GETTAG;
+ static {
+ Field f = null;
+ try {
+ f = JCTree.class.getDeclaredField("tag");
+ } catch (NoSuchFieldException e) {}
+ JCTREE_TAG = f;
+
+ Method m = null;
+ try {
+ m = JCTree.class.getDeclaredMethod("getTag");
+ } catch (NoSuchMethodException e) {}
+ JCTREE_GETTAG = m;
+ }
+
+ public static int getTag(JCTree node) {
+ if (JCTREE_GETTAG != null) {
+ try {
+ return (Integer) JCTREE_GETTAG.invoke(node);
+ } catch (Exception e) {}
+ }
+ try {
+ return (Integer) JCTREE_TAG.get(node);
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't get node tag");
+ }
+ }
+
}
diff --git a/test/transform/resource/after-delombok/DataOnLocalClass.java b/test/transform/resource/after-delombok/DataOnLocalClass.java
index ed4d30ca..abe2757b 100644
--- a/test/transform/resource/after-delombok/DataOnLocalClass.java
+++ b/test/transform/resource/after-delombok/DataOnLocalClass.java
@@ -63,7 +63,9 @@ class DataOnLocalClass2 {
String name;
@java.lang.SuppressWarnings("all")
public InnerLocal(@lombok.NonNull final String name) {
- if (name == null) throw new java.lang.NullPointerException("name");
+ if (name == null) {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
@lombok.NonNull
@@ -73,7 +75,9 @@ class DataOnLocalClass2 {
}
@java.lang.SuppressWarnings("all")
public void setName(@lombok.NonNull final String name) {
- if (name == null) throw new java.lang.NullPointerException("name");
+ if (name == null) {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
@java.lang.Override
diff --git a/test/transform/resource/after-delombok/NonNullOnParameter.java b/test/transform/resource/after-delombok/NonNullOnParameter.java
new file mode 100644
index 00000000..a27d19c9
--- /dev/null
+++ b/test/transform/resource/after-delombok/NonNullOnParameter.java
@@ -0,0 +1,48 @@
+class NonNullOnParameter extends Thread {
+ NonNullOnParameter(@lombok.NonNull String arg) {
+ this(arg, "");
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ }
+ NonNullOnParameter(@lombok.NonNull String arg, @lombok.NonNull String arg2) {
+ super(arg);
+ if (arg2 == null) {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ if (arg == null) throw new NullPointerException();
+ }
+ public void test2(@lombok.NonNull String arg, @lombok.NonNull String arg2, @lombok.NonNull String arg3) {
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if (arg3 == null) {
+ throw new java.lang.NullPointerException("arg3");
+ }
+ if (arg2 == null) {
+ throw new NullPointerException("arg2");
+ }
+ if (arg == null) System.out.println("Hello");
+ }
+ public void test3(@lombok.NonNull String arg) {
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if (arg != null) throw new IllegalStateException();
+ }
+ public void test(@lombok.NonNull String stringArg, @lombok.NonNull String arg2, @lombok.NonNull int primitiveArg) {
+ if (stringArg == null) {
+ throw new java.lang.NullPointerException("stringArg");
+ }
+ if (arg2 == null) {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ }
+ public void test(@lombok.NonNull String arg) {
+ if (arg == null) {
+ throw new java.lang.NullPointerException("arg");
+ }
+ System.out.println("Hey");
+ if (arg == null) throw new NullPointerException();
+ }
+}
diff --git a/test/transform/resource/after-delombok/NonNullPlain.java b/test/transform/resource/after-delombok/NonNullPlain.java
index 064e00b9..6b85cbf7 100644
--- a/test/transform/resource/after-delombok/NonNullPlain.java
+++ b/test/transform/resource/after-delombok/NonNullPlain.java
@@ -16,7 +16,9 @@ class NonNullPlain {
@java.beans.ConstructorProperties({"i", "s"})
@java.lang.SuppressWarnings("all")
public NonNullPlain(@lombok.NonNull final int i, @lombok.NonNull final String s) {
- if (s == null) throw new java.lang.NullPointerException("s");
+ if (s == null) {
+ throw new java.lang.NullPointerException("s");
+ }
this.i = i;
this.s = s;
}
@@ -45,7 +47,9 @@ class NonNullPlain {
@java.lang.SuppressWarnings("all")
public void setS(@lombok.NonNull final String s) {
- if (s == null) throw new java.lang.NullPointerException("s");
+ if (s == null) {
+ throw new java.lang.NullPointerException("s");
+ }
this.s = s;
}
diff --git a/test/transform/resource/after-delombok/SetterOnClass.java b/test/transform/resource/after-delombok/SetterOnClass.java
index 151bc179..7077c492 100644
--- a/test/transform/resource/after-delombok/SetterOnClass.java
+++ b/test/transform/resource/after-delombok/SetterOnClass.java
@@ -53,7 +53,9 @@ class SetterOnClass6 {
}
@java.lang.SuppressWarnings("all")
public void setNonNull(@lombok.NonNull final String nonNull) {
- if (nonNull == null) throw new java.lang.NullPointerException("nonNull");
+ if (nonNull == null) {
+ throw new java.lang.NullPointerException("nonNull");
+ }
this.nonNull = nonNull;
}
}
\ No newline at end of file
diff --git a/test/transform/resource/after-delombok/WitherOnClass.java b/test/transform/resource/after-delombok/WitherOnClass.java
index 783fede1..45d0c4b5 100644
--- a/test/transform/resource/after-delombok/WitherOnClass.java
+++ b/test/transform/resource/after-delombok/WitherOnClass.java
@@ -35,7 +35,9 @@ class WitherOnClass3 {
}
@java.lang.SuppressWarnings("all")
public WitherOnClass3 withNonNull(@lombok.NonNull final String nonNull) {
- if (nonNull == null) throw new java.lang.NullPointerException("nonNull");
+ if (nonNull == null) {
+ throw new java.lang.NullPointerException("nonNull");
+ }
return this.nonNull == nonNull ? this : new WitherOnClass3(this.couldBeNull, nonNull);
}
}
diff --git a/test/transform/resource/after-ecj/DataOnLocalClass.java b/test/transform/resource/after-ecj/DataOnLocalClass.java
index 137edf50..2f8dcca1 100644
--- a/test/transform/resource/after-ecj/DataOnLocalClass.java
+++ b/test/transform/resource/after-ecj/DataOnLocalClass.java
@@ -63,7 +63,9 @@ class DataOnLocalClass2 {
}
public @java.lang.SuppressWarnings("all") void setName(final @lombok.NonNull String name) {
if ((name == null))
- throw new java.lang.NullPointerException("name");
+ {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
@@ -96,7 +98,9 @@ class DataOnLocalClass2 {
public @java.lang.SuppressWarnings("all") InnerLocal(final @lombok.NonNull String name) {
super();
if ((name == null))
- throw new java.lang.NullPointerException("name");
+ {
+ throw new java.lang.NullPointerException("name");
+ }
this.name = name;
}
}
diff --git a/test/transform/resource/after-ecj/NonNullOnParameter.java b/test/transform/resource/after-ecj/NonNullOnParameter.java
new file mode 100644
index 00000000..bbceb153
--- /dev/null
+++ b/test/transform/resource/after-ecj/NonNullOnParameter.java
@@ -0,0 +1,61 @@
+class NonNullOnParameter extends Thread {
+ NonNullOnParameter(@lombok.NonNull String arg) {
+ this(arg, "");
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ }
+ NonNullOnParameter(@lombok.NonNull String arg, @lombok.NonNull String arg2) {
+ super(arg);
+ if ((arg2 == null))
+ {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ if ((arg == null))
+ throw new NullPointerException();
+ }
+ public void test2(@lombok.NonNull String arg, @lombok.NonNull String arg2, @lombok.NonNull String arg3) {
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if ((arg3 == null))
+ {
+ throw new java.lang.NullPointerException("arg3");
+ }
+ if ((arg2 == null))
+ {
+ throw new NullPointerException("arg2");
+ }
+ if ((arg == null))
+ System.out.println("Hello");
+ }
+ public void test3(@lombok.NonNull String arg) {
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ if ((arg != null))
+ throw new IllegalStateException();
+ }
+ public void test(@lombok.NonNull String stringArg, @lombok.NonNull String arg2, @lombok.NonNull int primitiveArg) {
+ if ((stringArg == null))
+ {
+ throw new java.lang.NullPointerException("stringArg");
+ }
+ if ((arg2 == null))
+ {
+ throw new java.lang.NullPointerException("arg2");
+ }
+ }
+ public void test(@lombok.NonNull String arg) {
+ if ((arg == null))
+ {
+ throw new java.lang.NullPointerException("arg");
+ }
+ System.out.println("Hey");
+ if ((arg == null))
+ throw new NullPointerException();
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-ecj/NonNullPlain.java b/test/transform/resource/after-ecj/NonNullPlain.java
index c9c96d0a..6e937f6a 100644
--- a/test/transform/resource/after-ecj/NonNullPlain.java
+++ b/test/transform/resource/after-ecj/NonNullPlain.java
@@ -8,7 +8,9 @@ import java.lang.annotation.*;
public @java.beans.ConstructorProperties({"i", "s"}) @java.lang.SuppressWarnings("all") NonNullPlain(final @lombok.NonNull int i, final @lombok.NonNull String s) {
super();
if ((s == null))
- throw new java.lang.NullPointerException("s");
+ {
+ throw new java.lang.NullPointerException("s");
+ }
this.i = i;
this.s = s;
}
@@ -26,7 +28,9 @@ import java.lang.annotation.*;
}
public @java.lang.SuppressWarnings("all") void setS(final @lombok.NonNull String s) {
if ((s == null))
- throw new java.lang.NullPointerException("s");
+ {
+ throw new java.lang.NullPointerException("s");
+ }
this.s = s;
}
public @java.lang.SuppressWarnings("all") void setO(final Object o) {
diff --git a/test/transform/resource/after-ecj/SetterOnClass.java b/test/transform/resource/after-ecj/SetterOnClass.java
index da928f24..aa3459bb 100644
--- a/test/transform/resource/after-ecj/SetterOnClass.java
+++ b/test/transform/resource/after-ecj/SetterOnClass.java
@@ -63,7 +63,9 @@
}
public @java.lang.SuppressWarnings("all") void setNonNull(final @lombok.NonNull String nonNull) {
if ((nonNull == null))
- throw new java.lang.NullPointerException("nonNull");
+ {
+ throw new java.lang.NullPointerException("nonNull");
+ }
this.nonNull = nonNull;
}
}
diff --git a/test/transform/resource/after-ecj/WitherOnClass.java b/test/transform/resource/after-ecj/WitherOnClass.java
index ff4566e5..82132e87 100644
--- a/test/transform/resource/after-ecj/WitherOnClass.java
+++ b/test/transform/resource/after-ecj/WitherOnClass.java
@@ -33,7 +33,9 @@
}
public @java.lang.SuppressWarnings("all") WitherOnClass3 withNonNull(final @lombok.NonNull String nonNull) {
if ((nonNull == null))
- throw new java.lang.NullPointerException("nonNull");
+ {
+ throw new java.lang.NullPointerException("nonNull");
+ }
return ((this.nonNull == nonNull) ? this : new WitherOnClass3(this.couldBeNull, nonNull));
}
}
diff --git a/test/transform/resource/before/NonNullOnParameter.java b/test/transform/resource/before/NonNullOnParameter.java
new file mode 100644
index 00000000..7eb4c565
--- /dev/null
+++ b/test/transform/resource/before/NonNullOnParameter.java
@@ -0,0 +1,30 @@
+class NonNullOnParameter extends Thread {
+ NonNullOnParameter(@lombok.NonNull String arg) {
+ this(arg, "");
+ }
+
+ NonNullOnParameter(@lombok.NonNull String arg, @lombok.NonNull String arg2) {
+ super(arg);
+ if (arg == null) throw new NullPointerException();
+ }
+
+ public void test2(@lombok.NonNull String arg, @lombok.NonNull String arg2, @lombok.NonNull String arg3) {
+ if (arg2 == null) {
+ throw new NullPointerException("arg2");
+ }
+ if (arg == null) System.out.println("Hello");
+ }
+
+ public void test3(@lombok.NonNull String arg) {
+ if (arg != null) throw new IllegalStateException();
+ }
+
+ public void test(@lombok.NonNull String stringArg, @lombok.NonNull String arg2, @lombok.NonNull int primitiveArg) {
+
+ }
+
+ public void test(@lombok.NonNull String arg) {
+ System.out.println("Hey");
+ if (arg == null) throw new NullPointerException();
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/messages-delombok/NonNullOnParameter.java.messages b/test/transform/resource/messages-delombok/NonNullOnParameter.java.messages
new file mode 100644
index 00000000..ac87adcd
--- /dev/null
+++ b/test/transform/resource/messages-delombok/NonNullOnParameter.java.messages
@@ -0,0 +1 @@
+22:89 @NonNull is meaningless on a primitive.
diff --git a/test/transform/resource/messages-delombok/NonNullPlain.java.messages b/test/transform/resource/messages-delombok/NonNullPlain.java.messages
new file mode 100644
index 00000000..67eb8abe
--- /dev/null
+++ b/test/transform/resource/messages-delombok/NonNullPlain.java.messages
@@ -0,0 +1 @@
+7:9 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/test/transform/resource/messages-ecj/NonNullOnParameter.java.messages b/test/transform/resource/messages-ecj/NonNullOnParameter.java.messages
new file mode 100644
index 00000000..1539929b
--- /dev/null
+++ b/test/transform/resource/messages-ecj/NonNullOnParameter.java.messages
@@ -0,0 +1,3 @@
+15:460 Dead code
+22:683 @NonNull is meaningless on a primitive.
+28:823 Dead code
\ No newline at end of file
diff --git a/test/transform/resource/messages-ecj/NonNullPlain.java.messages b/test/transform/resource/messages-ecj/NonNullPlain.java.messages
new file mode 100644
index 00000000..96eed252
--- /dev/null
+++ b/test/transform/resource/messages-ecj/NonNullPlain.java.messages
@@ -0,0 +1 @@
+7:116 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages b/test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages
new file mode 100644
index 00000000..fd23a32a
--- /dev/null
+++ b/test/transform/resource/messages-idempotent/NonNullOnParameter.java.messages
@@ -0,0 +1 @@
+33:89 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/test/transform/resource/messages-idempotent/NonNullPlain.java.messages b/test/transform/resource/messages-idempotent/NonNullPlain.java.messages
new file mode 100644
index 00000000..c48da311
--- /dev/null
+++ b/test/transform/resource/messages-idempotent/NonNullPlain.java.messages
@@ -0,0 +1,3 @@
+4:9 @NonNull is meaningless on a primitive.
+18:29 @NonNull is meaningless on a primitive.
+44:26 @NonNull is meaningless on a primitive.
\ No newline at end of file
diff --git a/usage_examples/NonNullExample_post.jpage b/usage_examples/NonNullExample_post.jpage
new file mode 100644
index 00000000..24175e06
--- /dev/null
+++ b/usage_examples/NonNullExample_post.jpage
@@ -0,0 +1,13 @@
+import lombok.NonNull;
+
+public class NonNullExample extends Something {
+ private String name;
+
+ public NonNullExample(@NonNull Person person) {
+ super("Hello");
+ if (person == null) {
+ throw new NullPointerException("person");
+ }
+ this.name = person.getName();
+ }
+}
diff --git a/usage_examples/NonNullExample_pre.jpage b/usage_examples/NonNullExample_pre.jpage
new file mode 100644
index 00000000..47556ce7
--- /dev/null
+++ b/usage_examples/NonNullExample_pre.jpage
@@ -0,0 +1,10 @@
+import lombok.NonNull;
+
+public class NonNullExample extends Something {
+ private String name;
+
+ public NonNullExample(@NonNull Person person) {
+ super("Hello");
+ this.name = person.getName();
+ }
+}
diff --git a/website/features/Cleanup.html b/website/features/Cleanup.html
index d1637dd4..a6e41f39 100644
--- a/website/features/Cleanup.html
+++ b/website/features/Cleanup.html
@@ -60,7 +60,7 @@
diff --git a/website/features/Data.html b/website/features/Data.html
index 8ace96cb..1c8510b7 100644
--- a/website/features/Data.html
+++ b/website/features/Data.html
@@ -75,7 +75,7 @@
diff --git a/website/features/Delegate.html b/website/features/Delegate.html
index 532f3f54..02cdf290 100644
--- a/website/features/Delegate.html
+++ b/website/features/Delegate.html
@@ -55,16 +55,15 @@
When passing classes to the annotation's types or excludes parameter, you cannot include generics.
This is a limitation of java. Use private inner interfaces or classes that extend the intended type including the
generics parameter to work around this problem.
-
-
+
When passing classes to the annotation, these classes do not need to be supertypes of the field. See the example.
-
-
+
@Delegate cannot be used on static fields or methods.
-
or: How I learned to stop worrying and love the NullPointerException.
+
+
Overview
+
+ NEW in Lombok 0.11.10: You can use @NonNull on the parameter of a method or constructor to have lombok generate a null-check statement for you.
+
+ Lombok has always treated any annotation named @NonNull on a field as a signal to generate a null-check if lombok generates an entire method or constructor for you, via
+ for example @Data. Now, however, using lombok's own @lombok.NonNull on a parameter results in the insertion of just the null-check
+ statement inside your own method or constructor.
+
+ The null-check looks like if (param == null) throw new NullPointerException("param"); and will be inserted at the very top of your method. For constructors, the null-check
+ will be inserted immediately following any explicit this() or super() calls.
+
+ If a null-check is already present at the top, no additional null-check will be generated.
+
+
+
+
+
With Lombok
+
@HTML_PRE@
+
+
+
+
Vanilla Java
+
@HTML_POST@
+
+
+
+
+
Small print
+
+ Lombok's detection scheme for already existing null-checks consists of scanning for if statements that look just like lombok's own. Any 'throws' statement as
+ the 'then' part of the if statement, whether in braces or not, counts. The conditional of the if statement must look exactly like PARAMNAME == null.
+ The first statement in your method that is not such a null-check stops the process of inspecting for null-checks.
+
+ While @Data and other method-generating lombok annotations will trigger on any annotation named @NonNull regardless of casing or package name,
+ this feature only triggers on lombok's own @NonNull annotation from the lombok package.
+
+ A @NonNull on a primitive parameter results in a warning. No null-check will be generated.
+
Because @SneakyThrows is an implementation detail and not part of your method signature, it is an error if you try to
declare a checked exception as sneakily thrown when you don't call any methods that throw this exception. (Doing so is perfectly legal
for throws statements to accommodate subclasses). Similarly, @SneakyThrows does not inherit.
@@ -72,7 +70,7 @@
All together now: A shortcut for @ToString, @EqualsAndHashCode,
@Getter on all fields, and @Setter on all non-final fields, and @RequiredArgsConstructor!
+ * Complete documentation is found at the project lombok features page for @Value.
+ *
+ * @see lombok.Getter
+ * @see lombok.experimental.FieldDefaults
+ * @see lombok.RequiredArgsConstructor
+ * @see lombok.ToString
+ * @see lombok.EqualsAndHashCode
+ * @see lombok.Data
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.SOURCE)
+public @interface Value {
+ /**
+ * If you specify a static constructor name, then the generated constructor will be private, and
+ * instead a static factory method is created that other classes can use to create instances.
+ * We suggest the name: "of", like so:
+ *
+ *
+ * public @Data(staticConstructor = "of") class Point { final int x, y; }
+ *
+ *
+ * Default: No static constructor, instead the normal constructor is public.
+ */
+ String staticConstructor() default "";
+}
diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java
new file mode 100644
index 00000000..4fd7b29d
--- /dev/null
+++ b/src/core/lombok/core/LombokInternalAliasing.java
@@ -0,0 +1,52 @@
+/*
+ * 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.core;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class LombokInternalAliasing {
+ public static final Map IMPLIED_EXTRA_STAR_IMPORTS;
+ public static final Map ALIASES;
+
+ /**
+ * Provide a fully qualified name (FQN), and the canonical version of this is returned.
+ */
+ public static String processAliases(String in) {
+ if (in == null) return null;
+ for (Map.Entry e : ALIASES.entrySet()) {
+ if (in.equals(e.getKey())) return e.getValue();
+ }
+ return in;
+ }
+
+ static {
+ Map m = new HashMap();
+ m.put("lombok.experimental", "lombok");
+ IMPLIED_EXTRA_STAR_IMPORTS = Collections.unmodifiableMap(m);
+
+ m = new HashMap();
+ m.put("lombok.experimental.Value", "lombok.Value");
+ ALIASES = Collections.unmodifiableMap(m);
+ }
+}
diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java
index a89091c4..c0e9dc43 100644
--- a/src/core/lombok/core/TypeLibrary.java
+++ b/src/core/lombok/core/TypeLibrary.java
@@ -74,6 +74,9 @@ public class TypeLibrary {
unqualifiedToQualifiedMap.put(unqualified, fullyQualifiedTypeName);
unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, fullyQualifiedTypeName);
+ for (Map.Entry e : LombokInternalAliasing.ALIASES.entrySet()) {
+ if (fullyQualifiedTypeName.equals(e.getValue())) unqualifiedToQualifiedMap.put(e.getKey(), fullyQualifiedTypeName);
+ }
}
/**
@@ -85,6 +88,9 @@ public class TypeLibrary {
public String toQualified(String typeReference) {
if (unqualifiedToQualifiedMap == null) {
if (typeReference.equals(unqualified) || typeReference.equals(qualified)) return qualified;
+ for (Map.Entry e : LombokInternalAliasing.ALIASES.entrySet()) {
+ if (e.getKey().equals(typeReference)) return e.getValue();
+ }
return null;
}
return unqualifiedToQualifiedMap.get(typeReference);
diff --git a/src/core/lombok/core/TypeResolver.java b/src/core/lombok/core/TypeResolver.java
index e2ba03b5..287a085f 100644
--- a/src/core/lombok/core/TypeResolver.java
+++ b/src/core/lombok/core/TypeResolver.java
@@ -39,19 +39,12 @@ public class TypeResolver {
this.imports = importList;
}
-// private static ImportList makeImportList(String packageString, Collection importStrings) {
-// Set imports = new HashSet();
-// if (packageString != null) imports.add(packageString + ".*");
-// imports.addAll(importStrings == null ? Collections.emptySet() : importStrings);
-// imports.add("java.lang.*");
-// return imports;
-// }
-//
public boolean typeMatches(LombokNode, ?, ?> context, String fqn, String typeRef) {
return typeRefToFullyQualifiedName(context, TypeLibrary.createLibraryForSingleType(fqn), typeRef) != null;
}
public String typeRefToFullyQualifiedName(LombokNode, ?, ?> context, TypeLibrary library, String typeRef) {
+ typeRef = LombokInternalAliasing.processAliases(typeRef);
// When asking if 'Foo' could possibly be referring to 'bar.Baz', the answer is obviously no.
String qualified = library.toQualified(typeRef);
if (qualified == null) return null;
diff --git a/src/core/lombok/eclipse/EclipseImportList.java b/src/core/lombok/eclipse/EclipseImportList.java
index 264ed91f..69246b3c 100644
--- a/src/core/lombok/eclipse/EclipseImportList.java
+++ b/src/core/lombok/eclipse/EclipseImportList.java
@@ -21,19 +21,21 @@
*/
package lombok.eclipse;
-import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.Eclipse.toQualifiedName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+
+import lombok.core.ImportList;
+import lombok.core.LombokInternalAliasing;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
-import lombok.core.ImportList;
-
public class EclipseImportList implements ImportList {
private ImportReference[] imports;
private ImportReference pkg;
@@ -53,13 +55,16 @@ public class EclipseImportList implements ImportList {
int len = token.length;
if (len != unqualified.length()) continue;
for (int i = 0; i < len; i++) if (token[i] != unqualified.charAt(i)) continue outer;
- return toQualifiedName(tokens);
+ return LombokInternalAliasing.processAliases(toQualifiedName(tokens));
}
}
return null;
}
@Override public boolean hasStarImport(String packageName) {
+ for (Map.Entry e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) {
+ if (e.getValue().equals(packageName) && hasStarImport(e.getKey())) return true;
+ }
if (isEqual(packageName, pkg)) return true;
if ("java.lang".equals(packageName)) return true;
if (imports != null) for (ImportReference imp : imports) {
diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java
index 60938649..0607137b 100644
--- a/src/core/lombok/eclipse/handlers/HandleValue.java
+++ b/src/core/lombok/eclipse/handlers/HandleValue.java
@@ -32,7 +32,7 @@ import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
import lombok.experimental.NonFinal;
-import lombok.experimental.Value;
+import lombok.Value;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java
index fbd4a518..d5d7460a 100644
--- a/src/core/lombok/javac/JavacImportList.java
+++ b/src/core/lombok/javac/JavacImportList.java
@@ -23,6 +23,7 @@ package lombok.javac;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Map;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@@ -32,6 +33,7 @@ import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.util.List;
import lombok.core.ImportList;
+import lombok.core.LombokInternalAliasing;
public class JavacImportList implements ImportList {
private final JCExpression pkg;
@@ -48,13 +50,18 @@ public class JavacImportList implements ImportList {
JCTree qual = ((JCImport) def).qualid;
if (!(qual instanceof JCFieldAccess)) continue;
String simpleName = ((JCFieldAccess) qual).name.toString();
- if (simpleName.equals(unqualified)) return qual.toString();
+ if (simpleName.equals(unqualified)) {
+ return LombokInternalAliasing.processAliases(qual.toString());
+ }
}
return null;
}
@Override public boolean hasStarImport(String packageName) {
+ for (Map.Entry e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) {
+ if (e.getValue().equals(packageName) && hasStarImport(e.getKey())) return true;
+ }
if (pkg != null && pkg.toString().equals(packageName)) return true;
if ("java.lang".equals(packageName)) return true;
diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java
index a59865f7..c0127f3c 100644
--- a/src/core/lombok/javac/handlers/HandleValue.java
+++ b/src/core/lombok/javac/handlers/HandleValue.java
@@ -26,7 +26,7 @@ import lombok.AccessLevel;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.NonFinal;
-import lombok.experimental.Value;
+import lombok.Value;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
@@ -45,7 +45,7 @@ import com.sun.tools.javac.tree.JCTree.JCModifiers;
@HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier.
public class HandleValue extends JavacAnnotationHandler {
@Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) {
- deleteAnnotationIfNeccessary(annotationNode, Value.class);
+ deleteAnnotationIfNeccessary(annotationNode, Value.class, lombok.experimental.Value.class);
JavacNode typeNode = annotationNode.up();
boolean notAClass = !isClass(typeNode);
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 92cebf4c..1784be90 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -280,7 +280,22 @@ public class JavacHandlerUtil {
* then removes any import statement that imports this exact annotation (not star imports).
* Only does this if the DeleteLombokAnnotations class is in the context.
*/
+ @SuppressWarnings("unchecked")
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class extends Annotation> annotationType) {
+ deleteAnnotationIfNeccessary0(annotation, annotationType);
+ }
+
+ /**
+ * Removes the annotation from javac's AST (it remains in lombok's AST),
+ * then removes any import statement that imports this exact annotation (not star imports).
+ * Only does this if the DeleteLombokAnnotations class is in the context.
+ */
+ @SuppressWarnings("unchecked")
+ public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class extends Annotation> annotationType1, Class extends Annotation> annotationType2) {
+ deleteAnnotationIfNeccessary0(annotation, annotationType1, annotationType2);
+ }
+
+ private static void deleteAnnotationIfNeccessary0(JavacNode annotation, Class extends Annotation>... annotationTypes) {
if (inNetbeansEditor(annotation)) return;
if (!annotation.shouldDeleteLombokAnnotations()) return;
JavacNode parentNode = annotation.directUp();
@@ -309,7 +324,9 @@ public class JavacHandlerUtil {
}
parentNode.getAst().setChanged();
- deleteImportFromCompilationUnit(annotation, annotationType.getName());
+ for (Class> annotationType : annotationTypes) {
+ deleteImportFromCompilationUnit(annotation, annotationType.getName());
+ }
}
public static void deleteImportFromCompilationUnit(JavacNode node, String name) {
diff --git a/src/delombok/lombok/delombok/DelombokApp.java b/src/delombok/lombok/delombok/DelombokApp.java
index 90a7b55e..5b97be08 100644
--- a/src/delombok/lombok/delombok/DelombokApp.java
+++ b/src/delombok/lombok/delombok/DelombokApp.java
@@ -84,6 +84,7 @@ public class DelombokApp extends LombokApp {
return null;
}
+ @SuppressWarnings("resource")
final JarFile toolsJarFile = new JarFile(toolsJar);
ClassLoader loader = new ClassLoader() {
diff --git a/test/transform/resource/after-delombok/ValueExperimental.java b/test/transform/resource/after-delombok/ValueExperimental.java
new file mode 100644
index 00000000..77a48ec9
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValueExperimental.java
@@ -0,0 +1,46 @@
+final class ValueExperimental1 {
+ @java.lang.SuppressWarnings("all")
+ public ValueExperimental1() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof ValueExperimental1)) return false;
+ return true;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ int result = 1;
+ return result;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "ValueExperimental1()";
+ }
+}
+final class ValueExperimental2 {
+ @java.lang.SuppressWarnings("all")
+ public ValueExperimental2() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof ValueExperimental2)) return false;
+ return true;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ int result = 1;
+ return result;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "ValueExperimental2()";
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-delombok/ValueExperimentalStarImport.java b/test/transform/resource/after-delombok/ValueExperimentalStarImport.java
new file mode 100644
index 00000000..6911f260
--- /dev/null
+++ b/test/transform/resource/after-delombok/ValueExperimentalStarImport.java
@@ -0,0 +1,25 @@
+import lombok.experimental.*;
+final class ValueExperimentalStarImport {
+ @java.lang.SuppressWarnings("all")
+ public ValueExperimentalStarImport() {
+
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof ValueExperimentalStarImport)) return false;
+ return true;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ int result = 1;
+ return result;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "ValueExperimentalStarImport()";
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-ecj/ValueExperimental.java b/test/transform/resource/after-ecj/ValueExperimental.java
new file mode 100644
index 00000000..dd13574a
--- /dev/null
+++ b/test/transform/resource/after-ecj/ValueExperimental.java
@@ -0,0 +1,39 @@
+import lombok.experimental.Value;
+final @Value class ValueExperimental1 {
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((! (o instanceof ValueExperimental1)))
+ return false;
+ return true;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ int result = 1;
+ return result;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return "ValueExperimental1()";
+ }
+ public @java.lang.SuppressWarnings("all") ValueExperimental1() {
+ super();
+ }
+}
+final @lombok.experimental.Value class ValueExperimental2 {
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((! (o instanceof ValueExperimental2)))
+ return false;
+ return true;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ int result = 1;
+ return result;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return "ValueExperimental2()";
+ }
+ public @java.lang.SuppressWarnings("all") ValueExperimental2() {
+ super();
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-ecj/ValueExperimentalStarImport.java b/test/transform/resource/after-ecj/ValueExperimentalStarImport.java
new file mode 100644
index 00000000..b69e85d9
--- /dev/null
+++ b/test/transform/resource/after-ecj/ValueExperimentalStarImport.java
@@ -0,0 +1,20 @@
+import lombok.experimental.*;
+final @Value class ValueExperimentalStarImport {
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((! (o instanceof ValueExperimentalStarImport)))
+ return false;
+ return true;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ int result = 1;
+ return result;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return "ValueExperimentalStarImport()";
+ }
+ public @java.lang.SuppressWarnings("all") ValueExperimentalStarImport() {
+ super();
+ }
+}
diff --git a/test/transform/resource/after-ecj/ValuePlain.java b/test/transform/resource/after-ecj/ValuePlain.java
index b798b308..d095913f 100644
--- a/test/transform/resource/after-ecj/ValuePlain.java
+++ b/test/transform/resource/after-ecj/ValuePlain.java
@@ -1,5 +1,5 @@
-import lombok.experimental.Value;
-final @lombok.experimental.Value class Value1 {
+import lombok.Value;
+final @lombok.Value class Value1 {
private final int x;
private final String name;
public @java.lang.SuppressWarnings("all") int getX() {
diff --git a/test/transform/resource/before/ValueExperimental.java b/test/transform/resource/before/ValueExperimental.java
new file mode 100644
index 00000000..6bae26a0
--- /dev/null
+++ b/test/transform/resource/before/ValueExperimental.java
@@ -0,0 +1,9 @@
+import lombok.experimental.Value;
+
+@Value
+class ValueExperimental1 {
+}
+
+@lombok.experimental.Value
+class ValueExperimental2 {
+}
\ No newline at end of file
diff --git a/test/transform/resource/before/ValueExperimentalStarImport.java b/test/transform/resource/before/ValueExperimentalStarImport.java
new file mode 100644
index 00000000..5f18cffe
--- /dev/null
+++ b/test/transform/resource/before/ValueExperimentalStarImport.java
@@ -0,0 +1,5 @@
+import lombok.experimental.*;
+
+@Value
+class ValueExperimentalStarImport {
+}
\ No newline at end of file
diff --git a/test/transform/resource/before/ValuePlain.java b/test/transform/resource/before/ValuePlain.java
index 39c583cc..3fe33705 100644
--- a/test/transform/resource/before/ValuePlain.java
+++ b/test/transform/resource/before/ValuePlain.java
@@ -1,5 +1,5 @@
-import lombok.experimental.Value;
-@lombok.experimental.Value class Value1 {
+import lombok.Value;
+@lombok.Value class Value1 {
final int x;
String name;
}
diff --git a/website/features/Data.html b/website/features/Data.html
index 1c8510b7..ad3aa892 100644
--- a/website/features/Data.html
+++ b/website/features/Data.html
@@ -75,7 +75,7 @@
diff --git a/website/features/SneakyThrows.html b/website/features/SneakyThrows.html
index 573bd95c..3b3987e4 100644
--- a/website/features/SneakyThrows.html
+++ b/website/features/SneakyThrows.html
@@ -70,7 +70,7 @@
diff --git a/website/features/Value.html b/website/features/Value.html
new file mode 100644
index 00000000..92fcc825
--- /dev/null
+++ b/website/features/Value.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+ @ExtensionMethod
+
+ @Value was introduced as experimental feature in lombok v0.11.4.
+
+ @Value no longer implies @Wither since lombok v0.11.8.
+
+ @Value promoted to the main lombok package since lombok v0.11.10.
+
+
+
Overview
+
+ @Value is the immutable variant of @Data; all fields are made private and final by default, and setters are not generated. The class itself is also made final by default, because immutability is not something that can be forced onto a subclass. Like @Data, useful toString(), equals() and hashCode() methods are also generated, each field gets a getter method, and a constructor that covers every
+ argument (except final fields that are initialized in the field declaration) is also generated.
+
+ In practice, @Value is shorthand for: final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter.
+
+ It is possible to override the final-by-default and private-by-default behaviour using either an explicit access level on a field, or by using the @NonFinal or @PackagePrivate annotations.
+ It is possible to override any default behaviour for any of the 'parts' that make up @Value by explicitly using that annotation.
+
+ For classes with generics, it's useful to have a static method which serves as a constructor, because inference of generic parameters via static methods works in java6 and avoids having to use the diamond operator. While you can force this by applying an explicit @AllArgsConstructor(staticConstructor="of") annotation, there's also the @Value(staticConstructor="of") feature, which will make the generated all-arguments constructor private, and generates a public static method named of which is a wrapper around this private constructor.
+
+ @Value was an experimental feature from v0.11.4 to v0.11.9 (as @lombok.experimental.Value). It has since been moved into the core package. The old annotation is still
+ around (and is an alias). It will eventually be removed in a future version, though.
+
- @Value was introduced as experimental feature in lombok v0.11.4.
-
- @Value no longer implies @Wither since lombok v0.11.8.
-
-
-
Experimental
-
- Experimental because:
-
-
Various choices still have to be vetted as being the correct 'least surprise' choice: Should the class be made final by default, etc.
-
- Current status: positive - Currently we feel this feature may move out of experimental status with no or minor changes soon.
-
-
-
Overview
-
- @Value is the immutable variant of @Data; all fields are made private and final by default, and setters are not generated. The class itself is also made final by default, because immutability is not something that can be forced onto a subclass. Like @Data, useful toString(), equals() and hashCode() methods are also generated, each field gets a getter method, and a constructor that covers every
- argument (except final fields that are initialized in the field declaration) is also generated.
-
- In practice, @Value is shorthand for: final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter.
-
- It is possible to override the final-by-default and private-by-default behaviour using either an explicit access level on a field, or by using the @NonFinal or @PackagePrivate annotations.
- It is possible to override any default behaviour for any of the 'parts' that make up @Value by explicitly using that annotation.
-
- For classes with generics, it's useful to have a static method which serves as a constructor, because inference of generic parameters via static methods works in java6 and avoids having to use the diamond operator. While you can force this by applying an explicit @AllArgsConstructor(staticConstructor="of") annotation, there's also the @Value(staticConstructor="of") feature, which will make the generated all-arguments constructor private, and generates a public static method named of which is a wrapper around this private constructor.
-
All together now: A shortcut for @ToString, @EqualsAndHashCode,
@Getter on all fields, and @Setter on all non-final fields, and @RequiredArgsConstructor!
--
cgit
From 867929c79f344e2fa63aa280cf452d39eb7aabf7 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Mon, 8 Jul 2013 06:06:49 +0200
Subject: added tests for new javadoc behaviour, javac implementation of
copying javadoc for getters/setters, and fixed pretty printer to no longer
inject an extra newline at the top of javadoc.
---
.../lombok/delombok/PrettyCommentsPrinter.java | 7 ++
.../after-delombok/CommentsInterspersed.java | 8 +-
.../after-delombok/DelegateWithDeprecated.java | 4 +-
.../resource/after-delombok/GetterDeprecated.java | 3 +
.../after-delombok/GetterSetterJavadoc.java | 100 +++++++++++++++++++++
.../resource/after-delombok/JavadocGenerally.java | 24 +++++
.../resource/after-delombok/SetterDeprecated.java | 3 +
.../resource/before/CommentsInterspersed.java | 8 +-
.../resource/before/DelegateWithDeprecated.java | 4 +-
.../resource/before/GetterSetterJavadoc.java | 37 ++++++++
.../resource/before/JavadocGenerally.java | 27 ++++++
11 files changed, 219 insertions(+), 6 deletions(-)
create mode 100644 test/transform/resource/after-delombok/GetterSetterJavadoc.java
create mode 100644 test/transform/resource/after-delombok/JavadocGenerally.java
create mode 100644 test/transform/resource/before/GetterSetterJavadoc.java
create mode 100644 test/transform/resource/before/JavadocGenerally.java
(limited to 'test/transform/resource/before')
diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
index 342e3323..1bb2ce23 100644
--- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
+++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
@@ -530,7 +530,14 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
print("/**"); println();
int pos = 0;
int endpos = lineEndPos(dc, pos);
+ boolean atStart = true;
while (pos < dc.length()) {
+ String line = dc.substring(pos, endpos);
+ if (line.trim().isEmpty() && atStart) {
+ atStart = false;
+ continue;
+ }
+ atStart = false;
align();
print(" *");
if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
diff --git a/test/transform/resource/after-delombok/CommentsInterspersed.java b/test/transform/resource/after-delombok/CommentsInterspersed.java
index 5aaafe42..833f2ce0 100644
--- a/test/transform/resource/after-delombok/CommentsInterspersed.java
+++ b/test/transform/resource/after-delombok/CommentsInterspersed.java
@@ -1,9 +1,13 @@
/* cmt *//* cmt2 */ /* cmt3 */ /*bla */
public class CommentsInterspersed {
- /** javadoc for field */
+ /**
+ * javadoc for field
+ */
private int x;
/* bla2 */ private String test = "foo"; //$NON-NLS-1$
- /** Javadoc on method */
+ /**
+ * Javadoc on method
+ */
public native void gwtTest(); /*-{
javascript;
}-*/
diff --git a/test/transform/resource/after-delombok/DelegateWithDeprecated.java b/test/transform/resource/after-delombok/DelegateWithDeprecated.java
index 04e12160..f7bd1e6d 100644
--- a/test/transform/resource/after-delombok/DelegateWithDeprecated.java
+++ b/test/transform/resource/after-delombok/DelegateWithDeprecated.java
@@ -3,7 +3,9 @@ class DelegateWithDeprecated {
private interface Bar {
@Deprecated
void deprecatedAnnotation();
- /** @deprecated */
+ /**
+ * @deprecated
+ */
void deprecatedComment();
void notDeprecated();
}
diff --git a/test/transform/resource/after-delombok/GetterDeprecated.java b/test/transform/resource/after-delombok/GetterDeprecated.java
index 3387540f..09ea9929 100644
--- a/test/transform/resource/after-delombok/GetterDeprecated.java
+++ b/test/transform/resource/after-delombok/GetterDeprecated.java
@@ -10,6 +10,9 @@ class GetterDeprecated {
public int getAnnotation() {
return this.annotation;
}
+ /**
+ * @deprecated
+ */
@java.lang.Deprecated
@java.lang.SuppressWarnings("all")
public int getJavadoc() {
diff --git a/test/transform/resource/after-delombok/GetterSetterJavadoc.java b/test/transform/resource/after-delombok/GetterSetterJavadoc.java
new file mode 100644
index 00000000..7bf92d1f
--- /dev/null
+++ b/test/transform/resource/after-delombok/GetterSetterJavadoc.java
@@ -0,0 +1,100 @@
+class GetterSetterJavadoc1 {
+ /**
+ * Some text
+ */
+ private int fieldName;
+ @java.lang.SuppressWarnings("all")
+ public GetterSetterJavadoc1() {
+ }
+ /**
+ * Getter section
+ *
+ * @return Sky is blue
+ */
+ @java.lang.SuppressWarnings("all")
+ public int getFieldName() {
+ return this.fieldName;
+ }
+ /**
+ * Some text
+ *
+ * @param fieldName Hello, World
+ */
+ @java.lang.SuppressWarnings("all")
+ public void setFieldName(final int fieldName) {
+ this.fieldName = fieldName;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof GetterSetterJavadoc1)) return false;
+ final GetterSetterJavadoc1 other = (GetterSetterJavadoc1)o;
+ if (!other.canEqual((java.lang.Object)this)) return false;
+ if (this.getFieldName() != other.getFieldName()) return false;
+ return true;
+ }
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof GetterSetterJavadoc1;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * PRIME + this.getFieldName();
+ return result;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "GetterSetterJavadoc1(fieldName=" + this.getFieldName() + ")";
+ }
+}
+class GetterSetterJavadoc2 {
+ /**
+ * Some text
+ */
+ private int fieldName;
+ /**
+ * Some text
+ *
+ * @return Sky is blue
+ */
+ @java.lang.SuppressWarnings("all")
+ public int getFieldName() {
+ return this.fieldName;
+ }
+ /**
+ * Some text
+ *
+ * @param fieldName Hello, World
+ */
+ @java.lang.SuppressWarnings("all")
+ public void setFieldName(final int fieldName) {
+ this.fieldName = fieldName;
+ }
+}
+class GetterSetterJavadoc3 {
+ /**
+ * Some text
+ */
+ private int fieldName;
+ /**
+ * Getter section
+ * @return Sky is blue
+ */
+ @java.lang.SuppressWarnings("all")
+ public int getFieldName() {
+ return this.fieldName;
+ }
+ /**
+ * Setter section
+ * @param fieldName Hello, World
+ */
+ @java.lang.SuppressWarnings("all")
+ public void setFieldName(final int fieldName) {
+ this.fieldName = fieldName;
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-delombok/JavadocGenerally.java b/test/transform/resource/after-delombok/JavadocGenerally.java
new file mode 100644
index 00000000..729cdce3
--- /dev/null
+++ b/test/transform/resource/after-delombok/JavadocGenerally.java
@@ -0,0 +1,24 @@
+/**
+ * Doc on package
+ */
+package testPackage;
+/** Weird doc */
+/**
+ * Doc on class
+ */
+class JavadocGenerally {
+ /**
+ * Doc on field
+ */
+ private int someField;
+ /**
+ * Doc on method
+ */
+ public void test() {
+ }
+ /**
+ * Doc on inner
+ */
+ public interface TestingInner {
+ }
+}
diff --git a/test/transform/resource/after-delombok/SetterDeprecated.java b/test/transform/resource/after-delombok/SetterDeprecated.java
index 5a6cf9f3..72a609ad 100644
--- a/test/transform/resource/after-delombok/SetterDeprecated.java
+++ b/test/transform/resource/after-delombok/SetterDeprecated.java
@@ -10,6 +10,9 @@ class SetterDeprecated {
public void setAnnotation(final int annotation) {
this.annotation = annotation;
}
+ /**
+ * @deprecated
+ */
@java.lang.Deprecated
@java.lang.SuppressWarnings("all")
public void setJavadoc(final int javadoc) {
diff --git a/test/transform/resource/before/CommentsInterspersed.java b/test/transform/resource/before/CommentsInterspersed.java
index e7898aaa..23a1060b 100644
--- a/test/transform/resource/before/CommentsInterspersed.java
+++ b/test/transform/resource/before/CommentsInterspersed.java
@@ -1,12 +1,16 @@
import /* cmt */ lombok./* cmt2 */Getter /* cmt3 */ ;
public /*bla */ class CommentsInterspersed {
- /** javadoc for field */
+ /**
+ * javadoc for field
+ */
private int x;
private /* bla2 */ @Getter String test = "foo"; //$NON-NLS-1$
- /** Javadoc on method */
+ /**
+ * Javadoc on method
+ */
public native void gwtTest(); /*-{
javascript;
}-*/
diff --git a/test/transform/resource/before/DelegateWithDeprecated.java b/test/transform/resource/before/DelegateWithDeprecated.java
index b748c6ec..064e951d 100644
--- a/test/transform/resource/before/DelegateWithDeprecated.java
+++ b/test/transform/resource/before/DelegateWithDeprecated.java
@@ -6,7 +6,9 @@ class DelegateWithDeprecated {
private interface Bar {
@Deprecated
void deprecatedAnnotation();
- /** @deprecated */
+ /**
+ * @deprecated
+ */
void deprecatedComment();
void notDeprecated();
}
diff --git a/test/transform/resource/before/GetterSetterJavadoc.java b/test/transform/resource/before/GetterSetterJavadoc.java
new file mode 100644
index 00000000..e3ae0aac
--- /dev/null
+++ b/test/transform/resource/before/GetterSetterJavadoc.java
@@ -0,0 +1,37 @@
+@lombok.Data
+class GetterSetterJavadoc1 {
+ /**
+ * Some text
+ *
+ * @param fieldName Hello, World
+ * --- GETTER ---
+ * Getter section
+ *
+ * @return Sky is blue
+ */
+ private int fieldName;
+}
+
+class GetterSetterJavadoc2 {
+ /**
+ * Some text
+ *
+ * @param fieldName Hello, World
+ * @return Sky is blue
+ */
+ @lombok.Getter @lombok.Setter private int fieldName;
+}
+
+class GetterSetterJavadoc3 {
+ /**
+ * Some text
+ *
+ * **SETTER**
+ * Setter section
+ * @param fieldName Hello, World
+ * **GETTER**
+ * Getter section
+ * @return Sky is blue
+ */
+ @lombok.Getter @lombok.Setter private int fieldName;
+}
diff --git a/test/transform/resource/before/JavadocGenerally.java b/test/transform/resource/before/JavadocGenerally.java
new file mode 100644
index 00000000..ee015acf
--- /dev/null
+++ b/test/transform/resource/before/JavadocGenerally.java
@@ -0,0 +1,27 @@
+/**
+ * Doc on package
+ */
+package testPackage;
+
+/** Weird doc */
+/**
+ * Doc on class
+ */
+class JavadocGenerally {
+ /**
+ * Doc on field
+ */
+ private int someField;
+
+ /**
+ * Doc on method
+ */
+ public void test() {
+ }
+
+ /**
+ * Doc on inner
+ */
+ public interface TestingInner {
+ }
+}
--
cgit
From 7af9add9996f2efab6cccc50c5503b3457534930 Mon Sep 17 00:00:00 2001
From: Reinier Zwitserloot
Date: Tue, 16 Jul 2013 00:51:31 +0200
Subject: * Fixed issues with @FieldDefaults and @Value (you can NOT override
@Value's final-by-default and private-by-default with it; now appropriate
warnings are emitted) * Builder now errors out on presence of most lombok
annotations on an explicit builder class. * Builder now takes
@FieldDefaults/@Value into account. * Builder on type now generates the
constructor as package private instead of private to avoid synthetic accessor
constructors. * added a bunch of test cases. * added a test case feature: If
the expected file is omitted entirely but there are expected messages, the
differences in the output itself are ignored. * streamlined checking for
boolean-ness (removed some duplicate code) * added 'fluent' and 'chain' to
@Builder.
---
src/core/lombok/core/TransformationsUtil.java | 25 ++++++++++++++--
.../eclipse/handlers/EclipseHandlerUtil.java | 35 ++++++++++++++++++++--
.../lombok/eclipse/handlers/HandleBuilder.java | 30 +++++++++++++++----
.../eclipse/handlers/HandleFieldDefaults.java | 10 ++++++-
src/core/lombok/eclipse/handlers/HandleGetter.java | 2 +-
src/core/lombok/eclipse/handlers/HandleSetter.java | 2 +-
src/core/lombok/eclipse/handlers/HandleWither.java | 2 +-
src/core/lombok/experimental/Builder.java | 16 ++++++++++
src/core/lombok/javac/handlers/HandleBuilder.java | 33 +++++++++++++++-----
.../lombok/javac/handlers/HandleFieldDefaults.java | 10 ++++++-
.../lombok/javac/handlers/JavacHandlerUtil.java | 29 +++++++++++++++++-
test/core/src/lombok/AbstractRunTests.java | 18 ++++++-----
.../after-delombok/BuilderChainAndFluent.java | 31 +++++++++++++++++++
.../resource/after-delombok/BuilderSimple.java | 2 +-
.../resource/after-ecj/BuilderChainAndFluent.java | 25 ++++++++++++++++
.../resource/after-ecj/BuilderSimple.java | 2 +-
.../resource/before/BuilderChainAndFluent.java | 4 +++
.../resource/before/BuilderInvalidUse.java | 18 +++++++++++
.../BuilderInvalidUse.java.messages | 2 ++
.../messages-ecj/BuilderInvalidUse.java.messages | 2 ++
website/features/Value.html | 5 +++-
website/features/experimental/Builder.html | 33 ++++++++++++--------
website/features/experimental/index.html | 2 +-
23 files changed, 290 insertions(+), 48 deletions(-)
create mode 100644 test/transform/resource/after-delombok/BuilderChainAndFluent.java
create mode 100644 test/transform/resource/after-ecj/BuilderChainAndFluent.java
create mode 100644 test/transform/resource/before/BuilderChainAndFluent.java
create mode 100644 test/transform/resource/before/BuilderInvalidUse.java
create mode 100644 test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages
create mode 100644 test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages
(limited to 'test/transform/resource/before')
diff --git a/src/core/lombok/core/TransformationsUtil.java b/src/core/lombok/core/TransformationsUtil.java
index 921c27d6..8959ad7a 100644
--- a/src/core/lombok/core/TransformationsUtil.java
+++ b/src/core/lombok/core/TransformationsUtil.java
@@ -22,13 +22,25 @@
package lombok.core;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.Value;
import lombok.experimental.Accessors;
+import lombok.experimental.FieldDefaults;
+import lombok.experimental.Wither;
/**
* Container for static utility methods useful for some of the standard lombok transformations, regardless of
@@ -39,6 +51,13 @@ public class TransformationsUtil {
//Prevent instantiation
}
+ @SuppressWarnings({"all", "unchecked", "deprecation"})
+ public static final List> INVALID_ON_BUILDERS = Collections.unmodifiableList(
+ Arrays.>asList(
+ Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class,
+ RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class,
+ Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class));
+
/**
* Given the name of a field, return the 'base name' of that field. For example, {@code fFoobar} becomes {@code foobar} if {@code f} is in the prefix list.
* For prefixes that end in a letter character, the next character must be a non-lowercase character (i.e. {@code hashCode} is not {@code ashCode} even if
@@ -159,12 +178,12 @@ public class TransformationsUtil {
if (fieldName.length() == 0) return null;
- Accessors ac = accessors.getInstance();
- fieldName = removePrefix(fieldName, ac.prefix());
+ Accessors ac = accessors == null ? null : accessors.getInstance();
+ fieldName = removePrefix(fieldName, ac == null ? new String[0] : ac.prefix());
if (fieldName == null) return null;
String fName = fieldName.toString();
- if (adhereToFluent && ac.fluent()) return fName;
+ if (adhereToFluent && ac != null && ac.fluent()) return fName;
if (isBoolean && fName.startsWith("is") && fieldName.length() > 2 && !Character.isLowerCase(fieldName.charAt(2))) {
// The field is for example named 'isRunning'.
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 364ce0a5..9bd634f7 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -22,6 +22,7 @@
package lombok.eclipse.handlers;
import static lombok.eclipse.Eclipse.*;
+import static lombok.core.TransformationsUtil.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -296,6 +297,29 @@ public class EclipseHandlerUtil {
}
+ public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) {
+ List disallowed = null;
+ for (EclipseNode child : typeNode.down()) {
+ for (Class extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ if (annotationTypeMatches(annType, child)) {
+ if (disallowed == null) disallowed = new ArrayList();
+ disallowed.add(annType.getSimpleName());
+ }
+ }
+ }
+
+ int size = disallowed == null ? 0 : disallowed.size();
+ if (size == 0) return;
+ if (size == 1) {
+ errorNode.addError("@" + disallowed.get(0) + " is not allowed on builder classes.");
+ return;
+ }
+ StringBuilder out = new StringBuilder();
+ for (String a : disallowed) out.append("@").append(a).append(", ");
+ out.setLength(out.length() - 2);
+ errorNode.addError(out.append(" are not allowed on builder classes.").toString());
+ }
+
public static Annotation copyAnnotation(Annotation annotation, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
@@ -845,15 +869,20 @@ public class EclipseHandlerUtil {
private static final Object MARKER = new Object();
static void registerCreatedLazyGetter(FieldDeclaration field, char[] methodName, TypeReference returnType) {
- if (!nameEquals(returnType.getTypeName(), "boolean") || returnType.dimensions() > 0) return;
- generatedLazyGettersWithPrimitiveBoolean.put(field, MARKER);
+ if (isBoolean(returnType)) {
+ generatedLazyGettersWithPrimitiveBoolean.put(field, MARKER);
+ }
+ }
+
+ public static boolean isBoolean(TypeReference typeReference) {
+ return nameEquals(typeReference.getTypeName(), "boolean") && typeReference.dimensions() == 0;
}
private static GetterMethod findGetter(EclipseNode field) {
FieldDeclaration fieldDeclaration = (FieldDeclaration) field.get();
boolean forceBool = generatedLazyGettersWithPrimitiveBoolean.containsKey(fieldDeclaration);
TypeReference fieldType = fieldDeclaration.type;
- boolean isBoolean = forceBool || (nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0);
+ boolean isBoolean = forceBool || isBoolean(fieldType);
EclipseNode typeNode = field.up();
for (String potentialGetterName : toAllGetterNames(field, isBoolean)) {
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index e2bf5fe2..70110a9c 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -58,13 +58,17 @@ import org.mangosdk.spi.ProviderFor;
import lombok.AccessLevel;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.core.TransformationsUtil;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
import lombok.experimental.Builder;
+import lombok.experimental.NonFinal;
@ProviderFor(EclipseAnnotationHandler.class)
+@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends EclipseAnnotationHandler {
@Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) {
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
@@ -99,14 +103,23 @@ public class HandleBuilder extends EclipseAnnotationHandler {
if (parent.get() instanceof TypeDeclaration) {
tdParent = parent;
TypeDeclaration td = (TypeDeclaration) tdParent.get();
- new HandleConstructor().generateAllArgsConstructor(tdParent, AccessLevel.PRIVATE, null, SkipIfConstructorExists.I_AM_BUILDER, Collections.emptyList(), ast);
+ List fields = new ArrayList();
+ @SuppressWarnings("deprecation")
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
+ // 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);
typesOfParameters.add(fd.type);
+ fields.add(fieldNode);
}
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, fields, null, SkipIfConstructorExists.I_AM_BUILDER, true, Collections.emptyList(), ast);
+
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
typeParams = td.typeParameters;
thrownExceptions = null;
@@ -181,11 +194,15 @@ public class HandleBuilder extends EclipseAnnotationHandler {
}
EclipseNode builderType = findInnerClass(tdParent, builderClassName);
- if (builderType == null) builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ if (builderType == null) {
+ builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ } else {
+ sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
+ }
List fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
List newMethods = new ArrayList();
for (EclipseNode fieldNode : fieldNodes) {
- MethodDeclaration newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast);
+ MethodDeclaration newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast, builderInstance.fluent(), builderInstance.chain());
if (newMethod != null) newMethods.add(newMethod);
}
@@ -315,7 +332,7 @@ public class HandleBuilder extends EclipseAnnotationHandler {
private static final AbstractMethodDeclaration[] EMPTY = {};
- private MethodDeclaration makeSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, ASTNode source) {
+ private MethodDeclaration makeSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, ASTNode source, boolean fluent, boolean chain) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY;
@@ -329,7 +346,10 @@ public class HandleBuilder extends EclipseAnnotationHandler {
if (Arrays.equals(name, existingName)) return null;
}
- return HandleSetter.createSetter(td, fieldNode, fieldNode.getName(), true, ClassFileConstants.AccPublic,
+ boolean isBoolean = isBoolean(fd.type);
+ String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean);
+
+ return HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
source, Collections.emptyList(), Collections.emptyList());
}
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
index 0d21fc08..d6d839cc 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
@@ -43,7 +43,7 @@ import org.mangosdk.spi.ProviderFor;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(EclipseAnnotationHandler.class)
-@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
+@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends EclipseAnnotationHandler {
public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -112,6 +112,14 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler
return;
}
+ if (level == AccessLevel.PACKAGE) {
+ annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
+ }
+
+ if (!makeFinal && annotation.isExplicit("makeFinal")) {
+ annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
+ }
+
if (node == null) return;
generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 760c595e..787f6f6c 100644
--- a/src/core/lombok/eclipse/handlers/HandleGetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleGetter.java
@@ -187,7 +187,7 @@ public class HandleGetter extends EclipseAnnotationHandler {
}
TypeReference fieldType = copyType(field.type, source);
- boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
+ boolean isBoolean = isBoolean(fieldType);
String getterName = toGetterName(fieldNode, isBoolean);
if (getterName == null) {
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index ae846a4e..3bfcc51c 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -159,7 +159,7 @@ public class HandleSetter extends EclipseAnnotationHandler {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = copyType(field.type, source);
- boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
+ boolean isBoolean = isBoolean(fieldType);
String setterName = toSetterName(fieldNode, isBoolean);
boolean shouldReturnThis = shouldReturnThis(fieldNode);
diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java
index 9d74cbd1..27fbc635 100644
--- a/src/core/lombok/eclipse/handlers/HandleWither.java
+++ b/src/core/lombok/eclipse/handlers/HandleWither.java
@@ -160,7 +160,7 @@ public class HandleWither extends EclipseAnnotationHandler {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = copyType(field.type, source);
- boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
+ boolean isBoolean = isBoolean(fieldType);
String witherName = toWitherName(fieldNode, isBoolean);
if (witherName == null) {
diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/experimental/Builder.java
index 5f2d1ca6..1300e7d3 100644
--- a/src/core/lombok/experimental/Builder.java
+++ b/src/core/lombok/experimental/Builder.java
@@ -118,4 +118,20 @@ public @interface Builder {
* Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}.
*/
String builderClassName() default "";
+
+ /**
+ * Normally the builder's 'set' methods are fluent, meaning, they have the same name as the field. Set this
+ * to {@code false} to name the setter method for field {@code someField}: {@code setSomeField}.
+ *
+ * Default: true
+ */
+ boolean fluent() default true;
+
+ /**
+ * Normally the builder's 'set' methods are chaining, meaning, they return the builder so that you can chain
+ * calls to set methods. Set this to {@code false} to have these 'set' methods return {@code void} instead.
+ *
+ * Default: true
+ */
+ boolean chain() default true;
}
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index aa485b26..6422f5ed 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -49,16 +49,19 @@ import com.sun.tools.javac.util.Name;
import lombok.AccessLevel;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.core.TransformationsUtil;
import lombok.experimental.Builder;
+import lombok.experimental.NonFinal;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
-
import static lombok.javac.Javac.*;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
@ProviderFor(JavacAnnotationHandler.class)
+@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends JavacAnnotationHandler {
@Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) {
Builder builderInstance = annotation.getInstance();
@@ -94,14 +97,22 @@ public class HandleBuilder extends JavacAnnotationHandler {
if (parent.get() instanceof JCClassDecl) {
tdParent = parent;
JCClassDecl td = (JCClassDecl) tdParent.get();
- new HandleConstructor().generateAllArgsConstructor(tdParent, AccessLevel.PRIVATE, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode);
-
+ ListBuffer allFields = ListBuffer.lb();
+ @SuppressWarnings("deprecation")
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
+ // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
+ // 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);
typesOfParameters.add(fd.vartype);
+ allFields.append(fieldNode);
}
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, true, annotationNode);
+
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
thrownExceptions = List.nil();
@@ -171,11 +182,15 @@ public class HandleBuilder extends JavacAnnotationHandler {
}
JavacNode builderType = findInnerClass(tdParent, builderClassName);
- if (builderType == null) builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ if (builderType == null) {
+ builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ } else {
+ sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
+ }
java.util.List fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
java.util.List newMethods = new ArrayList();
for (JavacNode fieldNode : fieldNodes) {
- JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast);
+ JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast, builderInstance.fluent(), builderInstance.chain());
if (newMethod != null) newMethods.add(newMethod);
}
@@ -281,16 +296,20 @@ public class HandleBuilder extends JavacAnnotationHandler {
}
- private JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JCTree source) {
+ private JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JCTree source, boolean fluent, boolean chain) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
+
for (JavacNode child : builderType.down()) {
if (child.getKind() != Kind.METHOD) continue;
Name existingName = ((JCMethodDecl) child.get()).name;
if (existingName.equals(fieldName)) return null;
}
+ boolean isBoolean = isBoolean(fieldNode);
+ String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean);
+
TreeMaker maker = builderType.getTreeMaker();
- return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, fieldName.toString(), true, source, List.nil(), List.nil());
+ return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.nil(), List.nil());
}
private JavacNode findInnerClass(JavacNode parent, String name) {
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index d32446c3..038f3e3f 100644
--- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
@@ -44,7 +44,7 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(JavacAnnotationHandler.class)
-@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
+@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends JavacAnnotationHandler {
public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -108,6 +108,14 @@ public class HandleFieldDefaults extends JavacAnnotationHandler {
return;
}
+ if (level == AccessLevel.PACKAGE) {
+ annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
+ }
+
+ if (!makeFinal && annotation.isExplicit("makeFinal")) {
+ annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
+ }
+
if (node == null) return;
generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index a24dad7d..d7d29da2 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -21,6 +21,7 @@
*/
package lombok.javac.handlers;
+import static lombok.core.TransformationsUtil.INVALID_ON_BUILDERS;
import static lombok.javac.Javac.*;
import java.lang.annotation.Annotation;
@@ -446,8 +447,12 @@ public class JavacHandlerUtil {
}
}
- private static boolean isBoolean(JavacNode field) {
+ public static boolean isBoolean(JavacNode field) {
JCExpression varType = ((JCVariableDecl) field.get()).vartype;
+ return isBoolean(varType);
+ }
+
+ public static boolean isBoolean(JCExpression varType) {
return varType != null && varType.toString().equals("boolean");
}
@@ -1065,6 +1070,28 @@ public class JavacHandlerUtil {
return maker.Ident(typeName);
}
+ public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(JavacNode typeNode, JavacNode errorNode) {
+ List disallowed = List.nil();
+ for (JavacNode child : typeNode.down()) {
+ for (Class extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ if (annotationTypeMatches(annType, child)) {
+ disallowed = disallowed.append(annType.getSimpleName());
+ }
+ }
+ }
+
+ int size = disallowed.size();
+ if (size == 0) return;
+ if (size == 1) {
+ errorNode.addError("@" + disallowed.head + " is not allowed on builder classes.");
+ return;
+ }
+ StringBuilder out = new StringBuilder();
+ for (String a : disallowed) out.append("@").append(a).append(", ");
+ out.setLength(out.length() - 2);
+ errorNode.addError(out.append(" are not allowed on builder classes.").toString());
+ }
+
static List copyAnnotations(List extends JCExpression> in) {
ListBuffer out = ListBuffer.lb();
for (JCExpression expr : in) {
diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java
index a80c7d8d..2f3f0988 100644
--- a/test/core/src/lombok/AbstractRunTests.java
+++ b/test/core/src/lombok/AbstractRunTests.java
@@ -68,10 +68,12 @@ public abstract class AbstractRunTests {
}
}
- StringReader r = new StringReader(expectedFile);
- BufferedReader br = new BufferedReader(r);
- String firstLine = br.readLine();
- if (firstLine != null && (firstLine.startsWith("//ignore") || params.shouldIgnoreBasedOnVersion(firstLine))) return false;
+ if (expectedFile != null) {
+ StringReader r = new StringReader(expectedFile);
+ BufferedReader br = new BufferedReader(r);
+ String firstLine = br.readLine();
+ if (firstLine != null && (firstLine.startsWith("//ignore") || params.shouldIgnoreBasedOnVersion(firstLine))) return false;
+ }
compare(
file.getName(),
@@ -91,7 +93,7 @@ public abstract class AbstractRunTests {
try {
reader = new BufferedReader(new FileReader(file));
} catch (FileNotFoundException e) {
- return "";
+ return null;
}
StringBuilder result = new StringBuilder();
String line;
@@ -104,7 +106,7 @@ public abstract class AbstractRunTests {
}
private String readFile(File dir, File file, boolean messages) throws IOException {
- if (dir == null) return "";
+ if (dir == null) return null;
return readFile(new File(dir, file.getName() + (messages ? ".messages" : "")));
}
@@ -140,7 +142,9 @@ public abstract class AbstractRunTests {
}
private void compare(String name, String expectedFile, String actualFile, List expectedMessages, LinkedHashSet actualMessages, boolean printErrors) throws Throwable {
- try {
+ if (expectedFile == null && expectedMessages.isEmpty()) expectedFile = "";
+
+ if (expectedFile != null) try {
compareContent(name, expectedFile, actualFile);
} catch (Throwable e) {
if (printErrors) {
diff --git a/test/transform/resource/after-delombok/BuilderChainAndFluent.java b/test/transform/resource/after-delombok/BuilderChainAndFluent.java
new file mode 100644
index 00000000..d4975bff
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderChainAndFluent.java
@@ -0,0 +1,31 @@
+class BuilderChainAndFluent {
+ private final int yes;
+ @java.lang.SuppressWarnings("all")
+ BuilderChainAndFluent(final int yes) {
+ this.yes = yes;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderChainAndFluentBuilder {
+ private int yes;
+ @java.lang.SuppressWarnings("all")
+ BuilderChainAndFluentBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public void setYes(final int yes) {
+ this.yes = yes;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderChainAndFluent build() {
+ return new BuilderChainAndFluent(yes);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderChainAndFluent.BuilderChainAndFluentBuilder(yes=" + this.yes + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static BuilderChainAndFluentBuilder builder() {
+ return new BuilderChainAndFluentBuilder();
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/after-delombok/BuilderSimple.java b/test/transform/resource/after-delombok/BuilderSimple.java
index 24ac369c..11c0e58c 100644
--- a/test/transform/resource/after-delombok/BuilderSimple.java
+++ b/test/transform/resource/after-delombok/BuilderSimple.java
@@ -5,7 +5,7 @@ class BuilderSimple {
private List also;
private int $butNotMe;
@java.lang.SuppressWarnings("all")
- private BuilderSimple(final int yes, final List also) {
+ BuilderSimple(final int yes, final List also) {
this.yes = yes;
this.also = also;
}
diff --git a/test/transform/resource/after-ecj/BuilderChainAndFluent.java b/test/transform/resource/after-ecj/BuilderChainAndFluent.java
new file mode 100644
index 00000000..6a307105
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderChainAndFluent.java
@@ -0,0 +1,25 @@
+@lombok.experimental.Builder(fluent = false,chain = false) class BuilderChainAndFluent {
+ public static @java.lang.SuppressWarnings("all") class BuilderChainAndFluentBuilder {
+ private int yes;
+ @java.lang.SuppressWarnings("all") BuilderChainAndFluentBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") void setYes(final int yes) {
+ this.yes = yes;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderChainAndFluent build() {
+ return new BuilderChainAndFluent(yes);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("BuilderChainAndFluent.BuilderChainAndFluentBuilder(yes=" + this.yes) + ")");
+ }
+ }
+ private final int yes;
+ @java.lang.SuppressWarnings("all") BuilderChainAndFluent(final int yes) {
+ super();
+ this.yes = yes;
+ }
+ public static @java.lang.SuppressWarnings("all") BuilderChainAndFluentBuilder builder() {
+ return new BuilderChainAndFluentBuilder();
+ }
+}
diff --git a/test/transform/resource/after-ecj/BuilderSimple.java b/test/transform/resource/after-ecj/BuilderSimple.java
index 228b1928..85db360d 100644
--- a/test/transform/resource/after-ecj/BuilderSimple.java
+++ b/test/transform/resource/after-ecj/BuilderSimple.java
@@ -25,7 +25,7 @@ import java.util.List;
private final int yes;
private List also;
private int $butNotMe;
- private @java.lang.SuppressWarnings("all") BuilderSimple(final int yes, final List also) {
+ @java.lang.SuppressWarnings("all") BuilderSimple(final int yes, final List also) {
super();
this.yes = yes;
this.also = also;
diff --git a/test/transform/resource/before/BuilderChainAndFluent.java b/test/transform/resource/before/BuilderChainAndFluent.java
new file mode 100644
index 00000000..4d08741b
--- /dev/null
+++ b/test/transform/resource/before/BuilderChainAndFluent.java
@@ -0,0 +1,4 @@
+@lombok.experimental.Builder(fluent = false, chain = false)
+class BuilderChainAndFluent {
+ private final int yes;
+}
diff --git a/test/transform/resource/before/BuilderInvalidUse.java b/test/transform/resource/before/BuilderInvalidUse.java
new file mode 100644
index 00000000..07f37d3d
--- /dev/null
+++ b/test/transform/resource/before/BuilderInvalidUse.java
@@ -0,0 +1,18 @@
+@lombok.experimental.Builder
+class BuilderInvalidUse {
+ private int something;
+
+ @lombok.Getter @lombok.Setter @lombok.experimental.FieldDefaults(makeFinal = true) @lombok.experimental.Wither @lombok.Data @lombok.ToString @lombok.EqualsAndHashCode
+ @lombok.AllArgsConstructor
+ public static class BuilderInvalidUseBuilder {
+
+ }
+}
+
+@lombok.experimental.Builder
+class AlsoInvalid {
+ @lombok.Value
+ public static class AlsoInvalidBuilder {
+
+ }
+}
\ No newline at end of file
diff --git a/test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages b/test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages
new file mode 100644
index 00000000..aeeb0c86
--- /dev/null
+++ b/test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages
@@ -0,0 +1,2 @@
+1:1 @Getter, @Setter, @Wither, @Data, @ToString, @EqualsAndHashCode, @AllArgsConstructor are not allowed on builder classes.
+12:1 @Value is not allowed on builder classes.
\ No newline at end of file
diff --git a/test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages b/test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages
new file mode 100644
index 00000000..8ffc6e26
--- /dev/null
+++ b/test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages
@@ -0,0 +1,2 @@
+1:0 @Getter, @Setter, @FieldDefaults, @Wither, @Data, @ToString, @EqualsAndHashCode, @AllArgsConstructor are not allowed on builder classes.
+12:331 @Value is not allowed on builder classes.
\ No newline at end of file
diff --git a/website/features/Value.html b/website/features/Value.html
index 92fcc825..e2cd8600 100644
--- a/website/features/Value.html
+++ b/website/features/Value.html
@@ -31,7 +31,7 @@
It is possible to override the final-by-default and private-by-default behaviour using either an explicit access level on a field, or by using the @NonFinal or @PackagePrivate annotations.
It is possible to override any default behaviour for any of the 'parts' that make up @Value by explicitly using that annotation.
-
+
@@ -54,6 +54,9 @@
@Value was an experimental feature from v0.11.4 to v0.11.9 (as @lombok.experimental.Value). It has since been moved into the core package. The old annotation is still
around (and is an alias). It will eventually be removed in a future version, though.
+
+ It is not possible to use @FieldDefaults to 'undo' the private-by-default and final-by-default aspect of fields in the annotated class. Use @NonFinal and @PackagePrivate on the fields in the class to override this behaviour.
+
diff --git a/website/features/experimental/index.html b/website/features/experimental/index.html
index 31fcd5ad..16d58050 100644
--- a/website/features/experimental/index.html
+++ b/website/features/experimental/index.html
@@ -23,7 +23,7 @@
as a core feature and move out of the experimental package.