-
Notifications
You must be signed in to change notification settings - Fork 39
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
Introduce FluxImplicitBlock
check
#472
Introduce FluxImplicitBlock
check
#472
Conversation
Looks good. No mutations were possible for these changes. |
2 similar comments
Looks good. No mutations were possible for these changes. |
Looks good. No mutations were possible for these changes. |
Thanks for filing this PR @benhalasi! The linked issue does suggest rewriting these methods, but also references FluxFlatMapUsage. I wonder whether we should go that same route here, and introduce a The
(Given our coding style, Picnic users would likely select option (1) or (2). If I'd find this in code I was personally responsible for, I'd likely add CC @Ernir for input as the original author of the issue. |
I see how I didn't get it right for the first time, will try to work it out and come back with something until Monday. |
No problem @benhalasi, it appears to be a bit more challenging than anticipated. In retrospect we maybe should've worked out this issue a bit further beforehand. If you have any questions, let us know 😄. We're happy to help. |
No rush! 💪 |
Flux.to{Stream,Iterable}
Refaster rulesImplicitBlockingFluxOperation
check
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
1 similar comment
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @benhalasi thanks for changing the implementation to this check! Will do a more thorough review probably tomorrow. Already left some comments to explain why the CI fails currently 😄.
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
...trib/src/test/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperationTest.java
Outdated
Show resolved
Hide resolved
...trib/src/test/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperationTest.java
Outdated
Show resolved
Hide resolved
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
Thank you for the fast feedback. Also here's the comment I forgot to attach: I tried to stick to the coding style I saw in other files as much as possible, but I'm sure there's a lot of room to improve. I'm the least confident in the comments/javadocs and the tests. I'm looking forward to any suggestion or change request, and also to answers to some questions: Code duplication between checkersI copied matchers from e.g. How to replace a method call with a method call chain.In the For this, originally, I wanted to use Then I tried to use one of the As I saw the best solution is to have a 'replacer' that is similar to the existing rename methods, but instead of a method name it expects (potentially chained) method calls. I didn't find such method and I wasn't sure enough about this to implement it without any feedback, so I went with a temporary - hacky - solution where I use the normal replace and prefix it with the part we would have lost. |
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
3 similar comments
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
I committed the changes @rickie suggested. I see some checks are still failing, I'm not able to reproduce them locally nor I want to spam further this PR so I leave it here until further feedback. The gust of the PR is already done I think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied matchers from e.g. FluxFlatMapUsage, is there a pattern to have these potentially shared matchers collected or the checkers considered to be fully independent from each-other and we don't care about these kinds of duplication?
Good question. If some Matchers
are used multiple times, we can consider adding them to MoreMatchers.java
. Maybe we should have a Reactor specific matchers file in error-prone-contrib
s tech.picnic.errorprone.bugpatterns.util
package 🤔.
All to say, yes we care about this kind of duplication and we want to avoid it 😄.
How to replace a method call with a method call chain.
In the getCollectAndBlockFix method I want to replace the matched method call with something like flux.collect(..).block()....
I think the CollectorMutability
check has some nice similarities that might be interesting. Based on that check I think you are on the the right path, because here you can see an example that does the same: creating a string and replacing that completely.
Didn't dive too deep but it seems like the approach is right.
Oh and btw, the Pitest plugin is complaining because a test is missing. We need almost the same test as the identificationWithoutGuavaOnClasspath
that is in CollectorMutabilityTest.java
. There needs to be a test that verifies that it doesn't perform the Guava fix if the classpath is empty, that is the reason there is a withClasspath()
method in the test I mention here. If you don't get around to adding that test, I'll do it when I do a proper review :).
Ahh there is now a failing test because of this line:
String flux = source.substring(0, source.indexOf("."));
I think this is because I changed flux().
with Flux.just(1)
in the tests and is currently a limitation of the implementation. This is indeed where you asked specifically for help. I think there might be even a better option that we can use. See an example here in the StringCaseLocaleUsage
check. It is also possible to specify specific positions where the fix should be applied. It might be smart to go this route.
A lot of things to consider. Feel free to play with the options 😄. Thanks for the good questions and I hope my answers make some things clear. I'll soon dive into this again myself. However, for today I need to focus on some other stuff. If you have questions, let us know!
I added a commit and applied some of the suggestions :).
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
public class ImplicitBlockingFluxOperation extends BugChecker | ||
implements MethodInvocationTreeMatcher { | ||
private static final long serialVersionUID = 1L; | ||
private static final Matcher<ExpressionTree> FLUX_TO_ITERABLE = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will push a change for this one. Now the check I have at the bottom of the class is not so nice, will come back to that. Maybe indeed we need this setup but utilizing the #namedAnyOf
makes a bit more sense to me. Maybe there is a easier way of checking whether we are making a SuggestedFix
for the toIterable
variant or not.
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
* merges `flux.collect(...).block()...` fix into given fix with specified collector and postfix | ||
* to match the original expression tree. | ||
* | ||
* @param collector expression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: this Javadoc is not really corresponding with the actual parameters 😉. Also, we add a .
after every sentence.
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
...trib/src/test/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperationTest.java
Outdated
Show resolved
Hide resolved
"", | ||
"class A {", | ||
" void m() {", | ||
" // BUG: Diagnostic contains:", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can simplify this testing setup like this:
" // BUG: Diagnostic contains:", | |
" // BUG: Diagnostic contains:", | |
"class A {", | |
" void m() {", | |
" Flux.just(1).toIterable();", | |
" Flux.just(2).toStream();", | |
" }") |
Additionally it would be nice to add some other test cases where we use toIterable
and toStream
on other objects then Flux
to show it doesn't flag those.
The answers and suggestions were very helpful, I did a fairly large rewrite and I got some additional questions too :D, mostly about assumptions I made, whether they are valid. I marked these with
|
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
e51b172
to
b46a6e9
Compare
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for applying the feedback @benhalasi . I see some really nice progress and a nice thought process 🚀!
I did a review but didn't yet get to the end. We are on the right path here!
I addressed most of your questions in my comments. However as my review didn't get till the end I wasn't able to apply to all questions yet.
Added a few commits 😄.
Let me answer most of your other questions:
assumes that the replacement expression will evaluate to a Mono<Collection<?>> - which is valid for the current cases, but could be misleading later, when extending this rule.
Good consideration; however, I think this is fine. In the case of toStream()
the resulting type will be exactly the same right, as we end with .stream()
.
if this is the way to go, how to test this behaviour?
No we generally use Optional
s for this if this is really necessary. However, in this exact case it probably wasn't necessary because we use Matcher
s to match quite specific expressions.
It replaces
method(args)
in a matchedClazz.optionally().chained().method(args)
expression.
Yes this sounds good, it should only change the part you mention. I didn't thoroughly check this part of the logic yet so exact review TBD 😉.
ASTHelpers.getStartPosition(tree)
returnsPosition.NOPOS
We should probably handle that case 😄.
I would guess the functionality of isTokenTheInvokedMethod and isClassValidSubstituteFor methods are already implemented and I just didn't find them, so these are naive implementations, may need some extra work if we go with them.
You are correct, worked on this and will push a suggestion!
Didn't find any other class with toStream or toIterable methods, should I just create an empty class for this, if so, where should I implement it?
Ah indeed, it was harder than expected. At the very least we should indeed have a simple class with a method .toStream()
and .toIterable()` to show it does nothing with those methods.
I didn't work on this yet, it seems like a separate issue from this one. Am I wrong and should include it here?
Hmm yeah that's true. If we move it we would also need to add tests to the respective class. We can defer it.
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION; | ||
import static com.google.errorprone.BugPattern.StandardTags.STYLE; | ||
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod; | ||
import static com.google.errorprone.util.ASTHelpers.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When running mvn clean install
, we can see that Error Prone flags star imports. See relevant documentation here.
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
" Flux.just(2).toStream();", | ||
" }", | ||
"}") | ||
.doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets write them like this:
.doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH); | |
.doTest(TestMode.TEXT_MATCH); |
Made an extra comment about this on an open ticket (I thought it was already there 😬), see here.
|
||
@Test | ||
void identificationWithoutGuavaOnClasspath() { | ||
CompilationTestHelper.newInstance(ImplicitBlockingFluxOperation.class, getClass()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, now that I'm seeing this I found out that it is actually a bit harder than expected.
Now we see that code is actually flagged by this check which could mean that the Guava fix is being applied. The CompilationTestHelper
test doesn't actually show us which fix is applied... We need to verify that in a specific case a specific SuggestedFix
is actually not applied.
This probably needs to be tested similar to how we test our util classes. See e.g. MoreJUnitMatchersTest.java
. This is a little more involved. Let me think if there is a more easy way to test this 🤔.
...trib/src/test/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperationTest.java
Outdated
Show resolved
Hide resolved
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
...-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/ImplicitBlockingFluxOperation.java
Outdated
Show resolved
Hide resolved
&& Objects.equals(token.name().toString(), getSymbol(tree).getQualifiedName().toString()); | ||
} | ||
|
||
// XXX: Replace with prewritten solution. (?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I addressed this XXX together with the next one. Indeed there is a solution provided out-of-the-box that we can use instead of these two methods. Error Prone doesn't have that good of a documentation that easily helps us identify such improvements. We can rely on the Types#isSubType
here, which allows us to do pretty cool stuff.
Will push a suggestion please take a look at it 😄. Let me know if you have any questions. I combined it with a usage of our own beautiful mini "DSL", see MoreTypes.java
. That allows us to greatly simplify the logic used here.
This might not be easy to get at first, so if you have any questions about it, let us know :).
ErrorProneTokens.getTokens(SourceCode.treeToString(tree, state), state.context); | ||
|
||
int treeStartPosition = getStartPosition(tree); | ||
int methodInvocationStartPosition = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice improvements done here!
I think there might be a solution where we don't need to use the ErrorProneToken
s. Usually we only go that route as "last resort" or if comments are involved. Now that I think of that, that could also be the case here. Maybe we should add test cases for that. See the StringCaseLocaleUsage
test cases for some examples. However, I'm not sure yet if that is the correct way to go about this either. I need to take some more time to dive into this before being able to give a good advice. For now I need to focus on some other things so will stop here :).
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
1 similar comment
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
Marked as ready for review because that's what we are doing 😉. |
b0eefdb
to
c8f1c5b
Compare
Co-authored-by: Rick Ossendrijver <[email protected]>
…gh `BugCheckerRefactoringTestHelper` Co-authored-by: Rick Ossendrijver <[email protected]>
matcher: - improve temp solution to handle cases when the matched methodSelect has multiple '.' chars in it. Like `Flux.just(...).toStream()` tests: - Add test `identificationWithoutGuavaOnClasspath` - Apply code-style suggestions/conventions - Can we get the type of the matched expression
- implement test for identification without guava on classpath
…der is enforced by other tests.
a60c4d1
to
56067cb
Compare
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed. |
ImplicitBlockingFlux
checkFluxImplicitBlock
check
fixes #468
Flags implicitly blocking operators on Reactor's
Flux
as fragile code.Flux#toStream
Suggested alternatives for
flux.toStream()
:flux.collect(toImmutableList()).block().stream();
flux.collect(toUnmodifiableList()).block().stream();
Flux#toIterable
Suggested alternatives for
flux.toIterable()
:flux.collect(toImmutableList()).block();
flux.collect(toUnmodifiableList()).block();