`kotlin:S6508` fires on `Mono<Void>` return types from Project Reactor

Hello,

I’d like to share a context where rule kotlin:S6508 fires on code that
follows the documented convention of an underlying library, in case it is
useful to the team.

Context

In Kotlin code that uses Project Reactor, the conventional return type for a
publisher that completes without emitting a value is Mono<Void>. This is
recommended by the Reactor Mono API documentation, which says (quoted from
the JavaDoc summary):

Mono<Void> should be used for Publisher that just completes without any
value. It is intended to be used in implementations and return types, input
parameters should keep using raw Publisher as much as possible.

Source: Mono (reactor-core 3.8.5)

Reactor operators such as .then() and Mono.when(...) produce Mono<Void>
values directly, and many Java-based libraries that integrate with Reactor
(Spring WebFlux among others) expose Mono<Void> in their public APIs.

My concern with Mono<Unit> as a replacement

As far as I can tell, Mono<Void> and Mono<Unit> differ semantically in
Reactor’s protocol:

  • Mono<Void> emits only onComplete or onError, never onNext.
  • Mono<Unit> emits a single Unit value via onNext before completing.

If that is correct, substituting Mono<Unit> changes the reactive signal that
downstream operators observe, and requires inserting .thenReturn(Unit) at
every call site that currently uses .then(). I am not a Reactor expert, so
the team may have a clearer view on whether this distinction matters in
practice.

Minimal reproducer

import reactor.core.publisher.Mono

class Example {
    // Conventional Reactor return type for a publisher that completes
    // without emitting a value. kotlin:S6508 is reported on `Mono<Void>`.
    fun process(input: Mono<String>): Mono<Void> =
        input
            .doOnNext { println(it) }
            .then()
}

Versions

  • SonarQube: Community Edition 26.1.0.118079
  • SonarQube for IDE: 12.2.2.84629 (connected to the SonarQube server above)
  • sonar-kotlin analyzer: (not sure where to find this; happy to provide it if you can point me to the right place in the analysis logs)
  • Kotlin: 1.9.25
  • reactor-core: 3.6.1

One possible direction

I noticed that pull request #677 (SONARKT-703) narrowed S6508 so it does not
fire on functions implementing parent class / interface signatures. I wonder
whether a similar approach could work here, for example not reporting when
Void appears as a type argument to a Reactor publisher type. That said, the
team knows the rule far better than I do, so I am just sharing the context and
happy to provide more examples if it would help.

Thanks for the work on the analyzer.

Hello @Laurent_T_Locala , welcome to the community and thanks for the detailed report!

Your concern is valid, and it is indeed a false positive. Our strategy for the Kotlin analyzer is currently to keep it as generic as possible and not bound to any specific libraries like Project Reactor, but we will investigate if it makes sense to disable this rule for all classes coming from Java libraries.

I’ve created a ticket on our side, and maybe it will make it to the next release of sonar-kotlin.

Best,

Peter