Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revisit nullability annotations #864

Merged
merged 3 commits into from
Sep 21, 2017
Merged

Conversation

sdeleuze
Copy link
Contributor

@sdeleuze sdeleuze commented Sep 15, 2017

WIP please do not merge yet, I have to review it with JetBrains, see pending remark and questions.

This commit introduces following changes:

  • It adds @NonNull and @Nullable annotations which allows to apply
    null-safety semantics on a specific type like JSR 305 but allows to
    target generic type arguments.
  • @NonNullApi does not apply to ElementType.TYPE anymore
  • Nullability annotations are now also applied to generic type arguments,
    varargs and array elements.
  • Package is now reactor.util.annotation
  • Add null-safety reference documentation

@sdeleuze sdeleuze added this to the 3.1.0.RELEASE milestone Sep 15, 2017
@sdeleuze sdeleuze self-assigned this Sep 15, 2017
@sdeleuze sdeleuze force-pushed the nullable-v2 branch 4 times, most recently from 999726e to c7b5e43 Compare September 19, 2017 14:04
@sdeleuze sdeleuze changed the title [WIP] Revisit nullability annotations Revisit nullability annotations Sep 19, 2017
@sdeleuze
Copy link
Contributor Author

@smaldini @simonbasle This pull request is now ready to be reviewed.

Setting default T nullability at class level as proposed by Stéphane is not supported yet, so better to use the more verbose form on each method for now. If supported later we should be able to define something like public abstract class Mono<T extends @NonNull Object>.

@simonbasle
Copy link
Member

I'm currently reviewing, seeing a few things to change so don't merge it yet

Copy link
Member

@simonbasle simonbasle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first pass, didn't review whole Flux and Mono but all the rest

@@ -33,7 +34,7 @@
* <p>
* The operator sanitizes the stacktrace and removes noisy entries such as:
* <ul>
* <li>java.lang.Thread entries</li>
* <li>java.annotation.Thread entries</li>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bad search-and-replace (IntelliJ tends to be too eager when refactoring, I always check the "occurrences in strings and comments" and most of the time reject that category from the refactor altogether)

@@ -361,7 +361,7 @@ default String operatorName() {
*
*/
@Nullable
default <T> T scan(Attr<T> key) {
default <T> T scan(Attr<@NonNull T> key) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Scannable.Attr generic's shouldn't be marked as @NonNull, as some attributes have a null default.

@@ -172,7 +173,7 @@
* @param instance the parent instance of the requested field
* @param isCancelled callback to detect cancellation
*/
public static <T, F> void postComplete(CoreSubscriber<? super T> actual,
public static <T, F> void postComplete(CoreSubscriber<? super @NonNull T> actual,
Queue<T> queue,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unlike other methods, queue and field here are not annotated. That said, I don't think AtomicLongFieldUpdater's T should be annotated (similar to Class<T>)

@@ -29,7 +29,7 @@
* Note that getting a stacktrace is a costly operation.
* <p>
* The operator sanitizes the stacktrace and removes noisy entries such as: <ul>
* <li>java.lang.Thread entries</li> <li>method references with source line of 1 (bridge
* <li>java.annotation.Thread entries</li> <li>method references with source line of 1 (bridge
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overeager search and replace

@@ -37,7 +37,7 @@
* <p>
* The operator sanitizes the stacktrace and removes noisy entries such as:
* <ul>
* <li>java.lang.Thread entries</li>
* <li>java.annotation.Thread entries</li>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overeager search and replace


/**
* Send 1 {@link Subscriber#onNext(Object) onNext} signal to the subscribers.
*
* @param value the item to emit (can be null if the relevant {@link Violation} is push)
* @return this {@link TestPublisher} for chaining.
*/
public abstract TestPublisher<T> next(@Nullable T value);
public abstract TestPublisher<@NonNull T> next(@Nullable T value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as sometimes value can be null, should probably not be annotated @NonNull

@@ -176,7 +177,7 @@
* @see #next(Object) next
*/
@SafeVarargs
public final TestPublisher<T> next(@Nullable T first, T... rest) {
public final TestPublisher<@NonNull T> next(@Nullable T first, T @Nullable ... rest) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as for next, can be containing nulls

@@ -194,7 +195,7 @@
* @see #complete() complete
*/
@SafeVarargs
public final TestPublisher<T> emit(T... values) {
public final TestPublisher<@NonNull T> emit(T @NonNull ... values) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as for next can be containing nulls, values are nullable

@@ -158,7 +158,7 @@ public void assertNotDroppedErrorsFailureOneDrop() {
fail("expected an AssertionError");
}
catch (AssertionError ae) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overeager search and replace all over the class (revert all changes)

@@ -371,7 +371,7 @@ public void errorMatchesInvalid() {
.expectNext("foo")
.expectErrorMatches(t -> t instanceof IllegalStateException)
.verify())
.withMessage("expectation \"expectErrorMatches\" failed (predicate failed on exception: java.lang.IllegalArgumentException)");
.withMessage("expectation \"expectErrorMatches\" failed (predicate failed on exception: java.annotation.IllegalArgumentException)");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overeager search and replace (revert all changes)

Copy link
Member

@simonbasle simonbasle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

finished review with Flux and Mono

@@ -5459,7 +5460,7 @@ public int getPrefetch() {
*
* @return a reduced {@link Flux}
*/
public final Mono<T> reduce(BiFunction<T, T, T> aggregator) {
public final Mono<@NonNull T> reduce(BiFunction<T, T, T> aggregator) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T, T, T not annotated NonNull

@@ -6841,7 +6842,7 @@ public final void subscribe(Subscriber<? super T> actual) {
*
* @return a new {@link Flux} that wait for source completion then emits from the supplied {@link Mono}
*/
public final <V> Mono<V> then(Mono<V> other) {
public final <V> Mono<@NonNull V> then(Mono<V> other) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parameter Mono<@NonNull T> other ?

@@ -7814,7 +7815,7 @@ public String toString() {
*
* @return the mono representing that Flux
*/
static <T> Mono<T> convertToMono(Callable<T> supplier) {
static <T> Mono<@NonNull T> convertToMono(Callable<@NonNull T> supplier) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here the Callable can return null

@@ -7834,7 +7835,7 @@ public String toString() {
}

@SafeVarargs
static <I> Flux<I> merge(int prefetch, boolean delayError, Publisher<? extends I>... sources) {
static <I> Flux<@NonNull I> merge(int prefetch, boolean delayError, Publisher<? extends I> @NonNull ... sources) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing nonnull on ? extends I

@@ -320,7 +321,7 @@
*
* @return A {@link Mono}.
*/
public static <T> Mono<T> fromCallable(Callable<? extends T> supplier) {
public static <T> Mono<@NonNull T> fromCallable(Callable<? extends @NonNull T> supplier) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromCallable now accepts callable returning null

@@ -464,7 +465,7 @@
*
* @return a {@link Mono}.
*/
public static <T> Mono<T> justOrEmpty(@Nullable Optional<? extends T> data) {
public static <T> Mono<@NonNull T> justOrEmpty(@Nullable Optional<? extends T> data) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would Optional's <T> need a Nullable? (but maybe Optional is not concerned as a core java type?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have indeed chosen to keep Optional generic type argument as it is because it is a system type.

@@ -1624,7 +1625,7 @@ public T block(Duration timeout) {
* @see #materialize()
* @see Signal
*/
public final Mono<T> doOnEach(Consumer<? super Signal<T>> signalConsumer) {
public final Mono<@NonNull T> doOnEach(Consumer<? super @NonNull Signal<@NonNull T>> signalConsumer) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doubt on Signal's <T> non-nullability (consider the case of a Signal.complete(), its T get() method would return null)

@@ -1717,7 +1718,7 @@ public T block(Duration timeout) {
*
* @return a new {@link Mono}
*/
public final Mono<T> doOnSubscribe(Consumer<? super Subscription> onSubscribe) {
public final Mono<@NonNull T> doOnSubscribe(Consumer<? super @NonNull Subscription> onSubscribe) {
Objects.requireNonNull(onSubscribe, "onSubscribe");
return doOnSignal(this, onSubscribe, null, null, null, null, null);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the doOnSuccessOrError below should have its BiConsumer generic types marked as Nullable both, like doAfterSuccessOrError

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong line I guess, I have done the update on the BiConsumer mentioned.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, couldn't comment on the line as it was not part of the diff, 👍

@@ -2268,7 +2269,7 @@ public T block(Duration timeout) {
* @return a {@link Mono} of materialized {@link Signal}
* @see #dematerialize()
*/
public final Mono<Signal<T>> materialize() {
public final Mono<@NonNull Signal<@NonNull T>> materialize() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once again same question about Signal's <T> non-nullability

@@ -3323,7 +3324,7 @@ public final void subscribe(Subscriber<? super T> actual) {
*
* @return a {@link CompletableFuture}
*/
public final CompletableFuture<T> toFuture() {
public final CompletableFuture<@NonNull T> toFuture() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future can contain null if the mono is empty

@sdeleuze
Copy link
Contributor Author

@simonbasle Thanks for these great feedbacks, I have pushed an updated version with fixes for all of your proposals.

This commit introduces following changes:
 - It adds @nonnull and @nullable annotations which allows to apply
null-safety semantics on a specific type like JSR 305 but allows to
target generic type arguments.
 - @NonNullApi does not apply to ElementType.TYPE anymore
 - Nullability annotations are now also applied to generic type arguments,
varargs and array elements.
 - Package is now reactor.util.annotation
 - Add null-safety reference documentation
@sdeleuze
Copy link
Contributor Author

I did an additional update including fixing broken test (GuideTests#debuggingActivated) and including correctly the documentation.

@codecov-io
Copy link

codecov-io commented Sep 19, 2017

Codecov Report

Merging #864 into master will decrease coverage by 0.05%.
The diff coverage is 73.33%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #864      +/-   ##
============================================
- Coverage     83.55%   83.49%   -0.06%     
+ Complexity     3278     3275       -3     
============================================
  Files           319      319              
  Lines         26052    26052              
  Branches       4840     4840              
============================================
- Hits          21767    21753      -14     
- Misses         2825     2832       +7     
- Partials       1460     1467       +7
Impacted Files Coverage Δ Complexity Δ
...n/java/reactor/core/publisher/FluxMapFuseable.java 85.93% <ø> (ø) 3 <0> (ø) ⬇️
.../java/reactor/core/publisher/EmitterProcessor.java 81.25% <ø> (ø) 80 <0> (ø) ⬇️
...re/src/main/java/reactor/util/function/Tuple2.java 100% <ø> (ø) 18 <0> (ø) ⬇️
...va/reactor/core/publisher/MonoStreamCollector.java 69.64% <ø> (ø) 2 <0> (ø) ⬇️
.../java/reactor/core/publisher/MonoPeekTerminal.java 60.46% <ø> (ø) 3 <0> (ø) ⬇️
...a/reactor/core/publisher/MonoSubscribeOnValue.java 76.47% <ø> (ø) 3 <0> (ø) ⬇️
...a/reactor/core/publisher/LambdaMonoSubscriber.java 89.39% <ø> (ø) 24 <0> (ø) ⬇️
.../main/java/reactor/core/publisher/FluxPublish.java 80.41% <ø> (ø) 12 <0> (ø) ⬇️
...ain/java/reactor/core/publisher/FluxRetryWhen.java 92.1% <ø> (ø) 4 <0> (ø) ⬇️
...reactor/core/publisher/ParallelFluxOnAssembly.java 76.47% <ø> (ø) 11 <0> (ø) ⬇️
... and 215 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f3e13bf...4c363f0. Read the comment docs.

@@ -423,7 +424,7 @@ public static void resetOnNextDropped() {
}

@Nullable
static BiFunction<? super Throwable, Object, ? extends Throwable> createOrUpdateOpErrorHook(Collection<BiFunction<? super Throwable, Object, ? extends Throwable>> hooks) {
static BiFunction<? super @NonNull Throwable, @Nullable Object, ? extends @NonNull Throwable> createOrUpdateOpErrorHook(Collection<@NonNull BiFunction<? super @NonNull Throwable, @NonNull Object, ? extends @NonNull Throwable>> hooks) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the right-hand side Object in the parameter BiFunction is also @Nullable ;)

@@ -335,7 +336,7 @@
* @param <T> type of the expected value
* @return A {@link Mono}.
*/
public static <T> Mono<T> fromCompletionStage(CompletionStage<? extends T> completionStage) {
public static <T> Mono<@NonNull T> fromCompletionStage(@Nullable CompletionStage<? extends @Nullable T> completionStage) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the @Nullable in fromCompletionStage(@Nullable

@@ -1717,7 +1718,7 @@ public T block(Duration timeout) {
*
* @return a new {@link Mono}
*/
public final Mono<T> doOnSubscribe(Consumer<? super Subscription> onSubscribe) {
public final Mono<@NonNull T> doOnSubscribe(Consumer<? super @NonNull Subscription> onSubscribe) {
Objects.requireNonNull(onSubscribe, "onSubscribe");
return doOnSignal(this, onSubscribe, null, null, null, null, null);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, couldn't comment on the line as it was not part of the diff, 👍

Copy link
Member

@simonbasle simonbasle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a couple things left and it'll be good to go ;)

@smaldini smaldini merged commit b597815 into reactor:master Sep 21, 2017
@sdeleuze sdeleuze deleted the nullable-v2 branch September 21, 2017 06:48
sdeleuze added a commit to sdeleuze/reactor-core that referenced this pull request Sep 21, 2017
sdeleuze added a commit to sdeleuze/reactor-core that referenced this pull request Sep 21, 2017
smaldini pushed a commit that referenced this pull request Sep 21, 2017
- Revert "Remove remaining javax nullability annotations"
- Revert "Revisit nullability annotations (#864)"
- Revisit nullability annotations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants