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

Rewrite Optional#orElse to Optional#orElseGet for non-constant expressions #527

Merged
merged 9 commits into from
May 26, 2023

Conversation

mlrprananta
Copy link
Contributor

@mlrprananta mlrprananta commented Mar 10, 2023

This is now on top of #599

Following an internal GH thread it felt fitting to write a rule to cover this case.

This PR proposes a Refaster rule to rewrite Optional#orElse to Optional#orElseGet for method invocation expressions, (with 2 or more args), with performance as the main benefit.

Introduce `OptionalOrElseGet` Refaster rule (#527)

While there, introduce `IsLikelyTrivialComputation` Matcher.

@mlrprananta mlrprananta requested a review from rickie March 10, 2023 14:45
@github-actions
Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

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

Clearly we need to do a bit more work here. To complicate matters further, this rule will introduce quite a lot of lambda expressions that we'd like to write as a method reference instead. So I wonder whether we should first finalize the (very hard) task of getting MethodReferenceUsage production ready. (I have a branch for that, but realistically it'll be months (or longer...) before I'll get back to that).

Added a small commit.

* Prefer {@link Optional#orElseGet(Supplier)} over {@link Optional#orElse(Object)} if the given
* value is not a compile-time constant.
*/
abstract static class OrElseToOrElseGet<T> {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
abstract static class OrElseToOrElseGet<T> {
static final class OrElseToOrElseGet<T> {

(This one is also suggested when running ./apply-error-prone-suggestions.sh.)

*/
abstract static class OrElseToOrElseGet<T> {
@BeforeTemplate
T before(Optional<T> o, @NotMatches(CompileTimeConstantExpressionMatcher.class) 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.

So it seems that perhaps CompileTimeConstantExpressionMatcher is a bit too strict. When we run apply-error-prone-suggestions.sh this diff is also generated:

diff --git a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/util/ConflictDetectionTest.java b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/util/ConflictDetectionTest.java
index 70282dd4..5b430448 100644
--- a/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/util/ConflictDetectionTest.java
+++ b/error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/util/ConflictDetectionTest.java
@@ -67,7 +67,7 @@ final class ConflictDetectionTest {
       return ConflictDetection.findMethodRenameBlocker(
               ASTHelpers.getSymbol(tree), tree.getName() + "t", state)
           .map(blocker -> buildDescription(tree).setMessage(blocker).build())
-          .orElse(Description.NO_MATCH);
+          .orElseGet(() -> Description.NO_MATCH);
     }
   }
 }

Similarly, as-is the rule triggers the following violation:

[WARN] /home/sschroevers/workspace/picnic/error-prone-support/refaster-support/src/main/java/tech/picnic/errorprone/refaster/AnnotatedCompositeCodeTransformer.java:[124,80] [Refaster Rule] OptionalRules.OrElseToOrElseGet: Refactoring opportunity
    (see https://error-prone.picnic.tech/refasterrules/OptionalRules#OrElseToOrElseGet)
  Did you mean 'return getAnnotationValue(Severity.class, Severity::value, delegate).orElseGet(()->SUGGESTION);'?

We might need a separate Matcher that allows any constant and field dereference 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, after running the script I did notice a lot of changes and some seemed unintended, in the end we're looking to prevent potentially expensive method invocations used with orElse, perhaps it would be a better to match based on method invocations instead?

Comment on lines 226 to 228
* Prefer {@link Optional#orElseGet(Supplier)} over {@link Optional#orElse(Object)} if the given
* value is not a compile-time constant.
Copy link
Member

Choose a reason for hiding this comment

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

I'll need to think a bit about phrasing, but likely we should call out that this rule can, in rare circumstances, change application behavior. (This can happen if value matches a non-pure expression.)

@github-actions
Copy link

Looks good. No mutations were possible for these changes.
Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie
Copy link
Member

rickie commented Mar 23, 2023

Hi @mlrprananta ,

I discussed this with @Stephan202 offline and we thought of an approach and are curious to hear what you think.

this rule will introduce quite a lot of lambda expressions that we'd like to write as a method reference instead

This is a blocker for enabling this rule internally. The MethodReferenceUsage check is a difficult one that will most likely not be worked on / finished any time soon. Therefore we need to think about our options:

  1. Just merge the rule (with improved Matcher), without enabling it internally. It's still a clear improvement and can be used by people outside of Picnic.
  2. Create a different matcher that matches when there are 2 or more method invocations present. Those cases cannot be rewritten to a method reference and are therefore an improvement that doesn't require additional (manual) effort. This rule can be enabled internally as well. We can add an XXX to say we have to improve when the MethodReferenceUsage check is finished.

WDYT? 😄

@mlrprananta
Copy link
Contributor Author

Hey @rickie, between those 2 options I would prefer the second route for now, as I don't feel comfortable not enabling it internally. Second route would be a easier to cover as well initially and as you mentioned we can improve it later on!

@rickie
Copy link
Member

rickie commented Mar 27, 2023

@mlrprananta sounds good to me! Let's go for that option 😄.

@github-actions
Copy link

github-actions bot commented Apr 5, 2023

Looks good. All 7 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsMethodInvocationWithTwoOrMoreArgs 0 7

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

1 similar comment
@github-actions
Copy link

github-actions bot commented Apr 5, 2023

Looks good. All 7 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsMethodInvocationWithTwoOrMoreArgs 0 7

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Comment on lines 161 to 167
return optional.map(predicate).orElse(Refaster.anyOf(false, Boolean.FALSE));
return Refaster.anyOf(
optional.map(predicate).orElse(false), optional.map(predicate).orElse(Boolean.FALSE));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Had to rewrite this as this was causing a "false" positive, orElse(Refaster.anyOf(false, Boolean.FALSE)) got rewritten to orElseGet(() -> Refaster.anyOf(false, Boolean.FALSE))

Copy link
Member

Choose a reason for hiding this comment

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

Doubting a little between this and adding a suppression 🤔. I'm leaning towards a suppression as this might raise questions, but let's see what @Stephan202 thinks.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, ideally the new matcher ignores Refaster.anyOf calls, since those aren't true method invocations.

Copy link
Member

Choose a reason for hiding this comment

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

... but the effort of doing this and properly testing it may not be worth it, so let's go with a suppression :)

@mlrprananta mlrprananta requested a review from Stephan202 April 5, 2023 07:53
@github-actions
Copy link

github-actions bot commented Apr 7, 2023

Looks good. All 7 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsMethodInvocationWithTwoOrMoreArgs 0 7

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

1 similar comment
@github-actions
Copy link

github-actions bot commented Apr 7, 2023

Looks good. All 7 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsMethodInvocationWithTwoOrMoreArgs 0 7

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Apr 8, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

@rickie rickie force-pushed the prefer-or-else-get branch from 229a3c0 to 0b73b98 Compare April 21, 2023 09:48
Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

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

Rebased and added a commit. There is the generalization of the Matcher left to also account for multiple method invocations :).

Cool stuff 🚀 !

Comment on lines 161 to 167
return optional.map(predicate).orElse(Refaster.anyOf(false, Boolean.FALSE));
return Refaster.anyOf(
optional.map(predicate).orElse(false), optional.map(predicate).orElse(Boolean.FALSE));
Copy link
Member

Choose a reason for hiding this comment

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

Doubting a little between this and adding a suppression 🤔. I'm leaning towards a suppression as this might raise questions, but let's see what @Stephan202 thinks.

// XXX: Extend rule to all method invocations (with less than 2 arguments).
static final class OptionalOrElseGet<T> {
@BeforeTemplate
T before(Optional<T> o, @Matches(IsMethodInvocationWithTwoOrMoreArgs.class) 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.

Suggested change
T before(Optional<T> o, @Matches(IsMethodInvocationWithTwoOrMoreArgs.class) T value) {
T before(Optional<T> optional, @Matches(IsMethodInvocationWithTwoOrMoreArgs.class) T value) {

This is used more often in the file, so lets change.

@Test
void matches() {
CompilationTestHelper.newInstance(
IsMethodInvocationWithTwoOrMoreArgsTest.MatcherTestChecker.class, getClass())
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
IsMethodInvocationWithTwoOrMoreArgsTest.MatcherTestChecker.class, getClass())
MatcherTestChecker.class, getClass())

We can drop the qualifier.

private static final long serialVersionUID = 1L;

/** Instantiates a new {@link IsMethodInvocationWithTwoOrMoreArgs} instance. */
public IsMethodInvocationWithTwoOrMoreArgs() {}
Copy link
Member

Choose a reason for hiding this comment

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

Interesting, here we discussed that we want to check for two MethodInvocations instead of one method invocation wit two arguments. This is not incorrect though. I think it is nice to combine the two things into one Matcher and call it something like: IsChainedMethodCallOrHasAtLeastTwoArgs. I tried implementing this but had some troubles with the VisitorState in the matches method.

In the AbstractMatcherTestChecker we only pass the VisitorState from the CompilationUnit which is not the state that we want. So we need to probably change that setup a bit. For now I'll flush the changes that I have.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting, here we discussed that we want to check for two MethodInvocations instead of one method invocation wit two arguments.

Ah, that was a misunderstanding on my part, but indeed both could apply for this situation. I can look into it a bit more to implement a (combined) matcher for chained method calls as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey @rickie, few questions:

  1. Could you elaborate on the VisitorState issue you mentioned? Which state should we be using instead?
  2. Could nested method invocations also apply for this situation? So something like:
m1(m2("param"))

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, the matcher shouldn't require >= 2 arguments: foo().bar() doesn't have any arguments, yet should be matched. One option is to use a TreeScanner (see examples in the code base) to count the number of MethodInvocationTrees.

IIUC the idea is to match non-trivial expressions that can't (or shouldn't) be replaced with a method reference. In this case we know that we're rewriting the expression to a Supplier<T>. So perhaps we're looking for a matcher of expressions which:

  1. Contain more than one method invocation; or
  2. Contain at least one method invocation with one or more arguments.

(Invocations of a single nullary method such as foo(), x.foo() or x.y.foo() should not be matched for now, since in those case a method reference may be preferable.)

That said, expressions of the form 42 * foo() should also be deferred.... So perhaps we should instead introduce an IsLikelyTrivialComputation matcher that does match true, x, foo(), x.foo(), x.y.foo() etc., and then have the Refaster rule use @NotMatches(IsLikelyTrivialComputation.class). Then later once MethodReferenceUsage works properly, we can replace this matcher with a simpler RequiresComputation matcher and then use @Matches(RequiresComputation.class).

Copy link
Member

Choose a reason for hiding this comment

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

In the AbstractMatcherTestChecker we only pass the VisitorState from the CompilationUnit which is not the state that we want. So we need to probably change that setup a bit. For now I'll flush the changes that I have.

I have a fix for this on a branch somewhere; let me try to open a PR for this ~soon.

Copy link
Member

@Stephan202 Stephan202 Apr 26, 2023

Choose a reason for hiding this comment

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

I filed #599. As stated there, my suggestion would be to rebase this branch on top of that branch.

Copy link
Contributor Author

@mlrprananta mlrprananta May 12, 2023

Choose a reason for hiding this comment

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

@Stephan202 Had some time to look at this again, and realized that I did not have a clear look at this before 😅

Indeed we're trying to only convert non-trivial expressions into Supplier<T>, and avoiding expressions that could be converted into a method reference.

I like the idea of the IsLikelyTrivialComputation matcher which would match trivial cases + method reference cases. However, IIUC, any expression that ends with a nullary method could in this case be converted into a method reference (even if it has 2 or more method invocations), e.g:

x() -> this::x
x().y() -> x()::y
x.y() -> x::y
x.y.z() -> x.y::z
x.y().z() -> x.y()::z
x.y("x").z() -> x.y("x")::z

etc.
This would mean the matcher should match any expressions that ends the chain with a nullary method invocation, right?

However I do see that IntelliJ mentions semantics might be changed when converting these "chain" expressions into method references, and perhaps it was already decided beforehand that we will not cover these cases for the method reference bug checker?

Copy link
Member

Choose a reason for hiding this comment

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

Nice examples @mlrprananta !

I checked in IntelliJ and saw that it mentions this optimization once you press alt+enter on the specific method. However, it doesn't highlight the method invocation to indicate you should rewrite it to a method reference, which for simple cases IntelliJ does. Because for the more difficult ones it doesn't do that, I would say it is fine for now to consider these things as a non trivial computation and we can rewrite them. Probably the last three examples will not occur that often.

So I think the fist three examples are on the "edge" of being a "trivial" computation... I'm not sure about those. The others can definitely be rewritten in the context of this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Discussed this with @Stephan202 offline;

We can stick to our original plan, the IsChainedMethodCallOrHasAtLeastTwoArgs matcher (name could be improved maybe) to flag two or more method invocations or when an invocation has two arguments.
This would flag the occurrences shown in the examples for which it is a little awkward to rewrite them to method references.
A problem is that there are many edge cases and questions that we can raise about the "boundary" of a IsLikelyTrivialComputation Matcher as you can go quite far with the things that you do or don't want to flag. Making it that difficult is probably not worth the time. Additionally it's less worth the effort as we should finish the MethodReferenceUsage check at some point anyways.

Copy link
Member

Choose a reason for hiding this comment

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

Alright, so I had a close look at this, and pushed a proposal 🙃

"",
" String positive2() {",
" // BUG: Diagnostic contains:",
" return String.format(\"%s\", \"foo\");",
Copy link
Member

Choose a reason for hiding this comment

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

We use the word "or more" for the Matcher, so we should add a test case :).

Will push some tweaks to simplify a little.

.addSourceLines(
"A.java",
"class A {",
" String foo(String foo, String bar) {",
Copy link
Member

Choose a reason for hiding this comment

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

Let's move these to the bottom :).


ImmutableSet<String> testOptionalOrElseGet() {
return ImmutableSet.of(
Optional.of("foo").orElse("bar"),
Copy link
Member

Choose a reason for hiding this comment

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

We make sure to use distinct values for every statement :).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does it need to be distinct for mutation testing?

Copy link
Member

Choose a reason for hiding this comment

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

Nope, it's about making sure that Refaster properly moves things around (which is easier to see if all values are distinct). Unfortunately for technical reasons our Refaster tests aren't exercised by Pitest.

@rickie rickie force-pushed the prefer-or-else-get branch 2 times, most recently from 5084a81 to 86c429c Compare April 21, 2023 09:50
@github-actions
Copy link

Looks good. All 4 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsMethodInvocationWithTwoOrMoreArgs 0 4

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

1 similar comment
@github-actions
Copy link

Looks good. All 4 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsMethodInvocationWithTwoOrMoreArgs 0 4

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

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

Interesting discussions!

Comment on lines 161 to 167
return optional.map(predicate).orElse(Refaster.anyOf(false, Boolean.FALSE));
return Refaster.anyOf(
optional.map(predicate).orElse(false), optional.map(predicate).orElse(Boolean.FALSE));
Copy link
Member

Choose a reason for hiding this comment

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

Ah, ideally the new matcher ignores Refaster.anyOf calls, since those aren't true method invocations.

private static final long serialVersionUID = 1L;

/** Instantiates a new {@link IsMethodInvocationWithTwoOrMoreArgs} instance. */
public IsMethodInvocationWithTwoOrMoreArgs() {}
Copy link
Member

Choose a reason for hiding this comment

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

Indeed, the matcher shouldn't require >= 2 arguments: foo().bar() doesn't have any arguments, yet should be matched. One option is to use a TreeScanner (see examples in the code base) to count the number of MethodInvocationTrees.

IIUC the idea is to match non-trivial expressions that can't (or shouldn't) be replaced with a method reference. In this case we know that we're rewriting the expression to a Supplier<T>. So perhaps we're looking for a matcher of expressions which:

  1. Contain more than one method invocation; or
  2. Contain at least one method invocation with one or more arguments.

(Invocations of a single nullary method such as foo(), x.foo() or x.y.foo() should not be matched for now, since in those case a method reference may be preferable.)

That said, expressions of the form 42 * foo() should also be deferred.... So perhaps we should instead introduce an IsLikelyTrivialComputation matcher that does match true, x, foo(), x.foo(), x.y.foo() etc., and then have the Refaster rule use @NotMatches(IsLikelyTrivialComputation.class). Then later once MethodReferenceUsage works properly, we can replace this matcher with a simpler RequiresComputation matcher and then use @Matches(RequiresComputation.class).

private static final long serialVersionUID = 1L;

/** Instantiates a new {@link IsMethodInvocationWithTwoOrMoreArgs} instance. */
public IsMethodInvocationWithTwoOrMoreArgs() {}
Copy link
Member

Choose a reason for hiding this comment

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

In the AbstractMatcherTestChecker we only pass the VisitorState from the CompilationUnit which is not the state that we want. So we need to probably change that setup a bit. For now I'll flush the changes that I have.

I have a fix for this on a branch somewhere; let me try to open a PR for this ~soon.


ImmutableSet<String> testOptionalOrElseGet() {
return ImmutableSet.of(
Optional.of("foo").orElse("bar"),
Copy link
Member

Choose a reason for hiding this comment

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

Nope, it's about making sure that Refaster properly moves things around (which is easier to see if all values are distinct). Unfortunately for technical reasons our Refaster tests aren't exercised by Pitest.

@rickie
Copy link
Member

rickie commented Apr 26, 2023

Nice answers @Stephan202 😄, curious to see the PR 🚀 !

@rickie rickie force-pushed the prefer-or-else-get branch from 86c429c to d61816d Compare April 26, 2023 15:28
@rickie rickie changed the base branch from master to sschroevers/improve-AbstractMatcherTestChecker April 26, 2023 15:29
@rickie
Copy link
Member

rickie commented Apr 26, 2023

I changed the base to #599 already 😄.

@Stephan202 Stephan202 added this to the 0.12.0 milestone May 20, 2023
Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

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

Added a commit; PTAL :)

(I'd be nice to get some reviews of the base PR 🙃)

Comment on lines 161 to 167
return optional.map(predicate).orElse(Refaster.anyOf(false, Boolean.FALSE));
return Refaster.anyOf(
optional.map(predicate).orElse(false), optional.map(predicate).orElse(Boolean.FALSE));
Copy link
Member

Choose a reason for hiding this comment

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

... but the effort of doing this and properly testing it may not be worth it, so let's go with a suppression :)

Comment on lines 125 to 129
Optional.of("foo").orElse("bar"),
Optional.of("baz").orElse(toString()),
Optional.of("qux").orElse(String.valueOf(true)),
Optional.of("quux").orElse(String.format("%s", "quuz")),
Optional.of("corge").orElse(String.format("%s", "grault", "garply")));
Copy link
Member

Choose a reason for hiding this comment

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

I guess we can do with fewer test cases :)

private static final long serialVersionUID = 1L;

/** Instantiates a new {@link IsMethodInvocationWithTwoOrMoreArgs} instance. */
public IsMethodInvocationWithTwoOrMoreArgs() {}
Copy link
Member

Choose a reason for hiding this comment

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

Alright, so I had a close look at this, and pushed a proposal 🙃

@github-actions
Copy link

Looks good. All 45 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation 0 45

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie rickie force-pushed the sschroevers/improve-AbstractMatcherTestChecker branch from 1d630d7 to 386fd74 Compare May 25, 2023 11:37
Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

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

One consideration, we already discussed that we don't want too many edge cases for this Matcher, so maybe it's already fine like this.

Anyway, really nice improvement! Curious to see how this performs on our codebase. 🚀 !

" }",
"",
" int negative6() {",
" return 1 * 2;",
Copy link
Member

Choose a reason for hiding this comment

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

For the BinaryTree we can check if both left and right are an LiteralTree or IdentifierTree as start? Also fine with leaving this as is.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, one could argue that this is a trivial computation. But then maybe so is 4 * 60 * 60 ("four hours"), which still wouldn't be flagged. And since the matcher is already quite complex, I'm not sure how far to go for diminishing returns. Even this case I don't expect to be triggered too frequently (at least in our internal code base).

Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

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

Tnx for the review @rickie. @mlrprananta we need one more review of #599 before we can merge this one. Are you up for that?

" }",
"",
" int negative6() {",
" return 1 * 2;",
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, one could argue that this is a trivial computation. But then maybe so is 4 * 60 * 60 ("four hours"), which still wouldn't be flagged. And since the matcher is already quite complex, I'm not sure how far to go for diminishing returns. Even this case I don't expect to be triggered too frequently (at least in our internal code base).

Base automatically changed from sschroevers/improve-AbstractMatcherTestChecker to master May 26, 2023 06:29
@rickie rickie force-pushed the prefer-or-else-get branch from a127136 to ead55db Compare May 26, 2023 07:02
@rickie
Copy link
Member

rickie commented May 26, 2023

Fair point --> Rebased --> Will merge.

@github-actions
Copy link

Looks good. All 45 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation 0 45

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

100.0% 100.0% Coverage
0.0% 0.0% Duplication

@rickie
Copy link
Member

rickie commented May 26, 2023

Updated the suggested commit message.

@Stephan202
Copy link
Member

The second like seems now less important than perhaps it is, but I lack creativity for a better proposal. Let's go 🚀

@rickie
Copy link
Member

rickie commented May 26, 2023

Yeah you are right, I tried combining them but the name is too long 😅.

@rickie rickie merged commit cc2c49e into master May 26, 2023
@rickie rickie deleted the prefer-or-else-get branch May 26, 2023 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants