Skip to content

Commit

Permalink
Introduce MonoOfPublishers bug check
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamedsamehsalah committed May 21, 2023
1 parent 3bbae43 commit d6838c0
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package tech.picnic.errorprone.bugpatterns;

import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.FRAGILE_CODE;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.generic;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.subOf;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.type;
import static tech.picnic.errorprone.bugpatterns.util.MoreTypes.unbound;

import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;

/**
* A {@link BugChecker} that flags nesting of {@link Publisher Publishers} inside {@link Mono
* Monos}.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary =
"Avoid nesting `Publisher`s inside `Mono`s; the resultant code is hard to reason about",
link = BUG_PATTERNS_BASE_URL + "MonoOfPublishers",
linkType = CUSTOM,
severity = WARNING,
tags = FRAGILE_CODE)
public final class MonoOfPublishers extends BugChecker implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Supplier<Type> MONO =
Suppliers.typeFromString("reactor.core.publisher.Mono");
private static final Supplier<Type> MONO_OF_PUBLISHERS =
VisitorState.memoize(
generic(MONO, subOf(generic(type("org.reactivestreams.Publisher"), unbound()))));

/** Instantiates a new {@link MonoOfPublishers} instance. */
public MonoOfPublishers() {}

@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
Type type = MONO_OF_PUBLISHERS.get(state);
if (type == null || !state.getTypes().isSubtype(ASTHelpers.getType(tree), type)) {
return Description.NO_MATCH;
}

return describeMatch(tree);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ Flux<S> after(Flux<T> flux) {
/** Prefer {@link Mono#flatMap(Function)} over more contrived alternatives. */
static final class MonoFlatMap<S, T> {
@BeforeTemplate
@SuppressWarnings("MonoOfPublishers")
Mono<T> before(Mono<S> mono, Function<? super S, ? extends Mono<? extends T>> function) {
return mono.map(function).flatMap(identity());
}
Expand All @@ -795,6 +796,7 @@ Mono<T> after(Mono<S> mono, Function<? super S, ? extends Mono<? extends T>> fun
/** Prefer {@link Mono#flatMapMany(Function)} over more contrived alternatives. */
static final class MonoFlatMapMany<S, T> {
@BeforeTemplate
@SuppressWarnings("MonoOfPublishers")
Flux<T> before(Mono<S> mono, Function<? super S, ? extends Publisher<? extends T>> function) {
return mono.map(function).flatMapMany(identity());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package tech.picnic.errorprone.bugpatterns;

import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;

final class MonoOfPublishersTest {
@Test
void identification() {
CompilationTestHelper.newInstance(MonoOfPublishers.class, getClass())
.addSourceLines(
"A.java",
"import reactor.core.publisher.Flux;",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" Mono.empty();",
" Mono.just(1);",
" // BUG: Diagnostic contains:",
" Mono.just(Mono.empty());",
" // BUG: Diagnostic contains:",
" Mono.just(Mono.just(1));",
" // BUG: Diagnostic contains:",
" Mono.just(Flux.empty());",
" // BUG: Diagnostic contains:",
" Mono.just(Flux.just(1));",
" // BUG: Diagnostic contains:",
" Mono.just(1).map(Mono::just);",
" // BUG: Diagnostic contains:",
" Mono.just(1).map(Flux::just);",
"",
" Mono.justOrEmpty(null);",
" // BUG: Diagnostic contains:",
" Mono.justOrEmpty(Mono.just(1));",
" // BUG: Diagnostic contains:",
" Mono.justOrEmpty(Flux.just(1));",
" // BUG: Diagnostic contains:",
" Mono.justOrEmpty(1).map(Mono::just);",
" // BUG: Diagnostic contains:",
" Mono.justOrEmpty(1).map(Flux::just);",
" }",
"}")
.doTest();
}
}

0 comments on commit d6838c0

Please sign in to comment.