Compare commits

...

3 Commits

Author SHA1 Message Date
mohamedsamehsalah
aa5d885e19 2nd Draft 2023-09-10 19:00:09 +02:00
mohamedsamehsalah
38066b262c Fix test mutation issues 2023-09-10 19:00:09 +02:00
mohamedsamehsalah
c1f5637d29 Introduce MonoFilterThen bug check 2023-09-10 19:00:09 +02:00
2 changed files with 142 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.errorprone.BugPattern.LinkType.CUSTOM;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.BugPattern.StandardTags.LIKELY_ERROR;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;
import static tech.picnic.errorprone.bugpatterns.util.Documentation.BUG_PATTERNS_BASE_URL;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
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.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.util.Position;
/**
* Each Reactor operator has some set of properties, and some of those properties are incompatible
* when combined. This {@link BugChecker} is used to flag such cases.
*/
@AutoService(BugChecker.class)
@BugPattern(
summary =
"The reactor operators used are incompatible with each other and can indicate a hidden bug.",
link = BUG_PATTERNS_BASE_URL + "IllogicalReactorOperators",
linkType = CUSTOM,
severity = WARNING,
tags = LIKELY_ERROR)
public final class IllogicalReactorOperators extends BugChecker
implements MethodInvocationTreeMatcher {
private static final long serialVersionUID = 1L;
private static final Supplier<Type> PUBLISHER =
Suppliers.typeFromString("org.reactivestreams.Publisher");
private static final ImmutableSet<String> INCOMPATIBLE_CHILD_OPERATORS =
ImmutableSet.of("filter", "map", "flatMap", "concatMap");
private static final Matcher<ExpressionTree> PUBLISHER_INCOMPATIBLE_OPS =
instanceMethod().onDescendantOf(PUBLISHER).namedAnyOf("then", "thenEmpty", "thenMany");
/** Instantiates a new {@link IllogicalReactorOperators} instance. */
public IllogicalReactorOperators() {}
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
if (!(PUBLISHER_INCOMPATIBLE_OPS.matches(tree, state)
&& INCOMPATIBLE_CHILD_OPERATORS.stream()
.anyMatch(
ASTHelpers.getSymbol(ASTHelpers.getReceiver(tree)).getSimpleName().toString()
::equals))) {
return Description.NO_MATCH;
}
Description.Builder description = buildDescription(tree);
ImmutableList<ExpressionTree> collect =
ASTHelpers.streamReceivers(tree).collect(toImmutableList());
SuggestedFix fix = dropNoOpOperator(collect.get(0), state).build();
description.addFix(fix);
return description.build();
}
private static SuggestedFix.Builder dropNoOpOperator(
ExpressionTree expressionTree, VisitorState state) {
int startPosition = state.getEndPosition(ASTHelpers.getReceiver(expressionTree));
int endPosition = state.getEndPosition(expressionTree);
checkState(
startPosition != Position.NOPOS && endPosition != Position.NOPOS,
"Cannot locate method to be replaced in source code");
return SuggestedFix.builder().replace(startPosition, endPosition, "");
}
}

View File

@@ -0,0 +1,53 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
final class IllogicalReactorOperatorsTest {
@Test
void identification() {
CompilationTestHelper.newInstance(IllogicalReactorOperators.class, getClass())
.addSourceLines(
"A.java",
"import reactor.core.publisher.Mono;",
"",
"class A {",
" void m() {",
" Mono.just(1).then(Mono.just(2));",
" Mono.just(1).filter(i -> i != 1).when(Mono.just(2));",
" // BUG: Diagnostic contains:",
" Mono.just(1).filter(i -> i != 1).then();",
" // BUG: Diagnostic contains:",
" Mono.just(1).filter(i -> i != 1).then(Mono.just(2));",
" }",
"}")
.doTest();
}
@Test
void suggestedFix() {
BugCheckerRefactoringTestHelper.newInstance(IllogicalReactorOperators.class, getClass())
.addInputLines(
"A.java",
"import reactor.core.publisher.Mono;",
"",
"class A {",
"",
" void m() {",
" Mono.just(1).filter(i -> i != 1).then(Mono.just(2));",
" }",
"}")
.addOutputLines(
"A.java",
"import reactor.core.publisher.Mono;",
"",
"class A {",
"",
" void m() {",
" Mono.just(1).then(Mono.just(2));",
" }",
"}")
.doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);
}
}