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

Extend StreamRules Refaster rule collection #593

Merged

Conversation

mohamedsamehsalah
Copy link
Contributor

@mohamedsamehsalah mohamedsamehsalah commented Apr 23, 2023

#578

  • Replace redundant stream#collect calls.

Suggested commit message:

Extend `StreamRules` Refaster rule collection (#593)

All changes suggested by SonarCloud's java:S4266 rule are now covered.

See https://sonarcloud.io/organizations/picnic-technologies/rules?open=java%3AS4266&rule_key=java%3AS4266

Fixes #578.
  1. stream.collect(minBy(comparator)) -> stream.min(comparator)
  2. stream.collect(maxBy(comparator)) -> stream.max(comparator)
  3. stream.collect(summingInt(mapper)) -> stream.mapToInt(mapper).sum()
  4. stream.collect(summingDouble(mapper)) -> stream.mapToDouble(mapper).sum()
  5. stream.collect(summingLong(mapper)) -> stream.mapToLong(mapper).sum()
  6. stream.collect(counting()) -> stream.count()
  7. stream.collect(reducing(identity, accumulator)) -> stream.reduce(identity, accumulator)
  8. stream.collect(reducing(accumulator)) -> stream.reduce(accumulator)
  9. stream.collect(mapping(mapper, collector)) -> stream.map(mapper).collect(collector)

Fun fact: Most of this list was suggested by co-pilot 🧠 :
Screenshot 2023-04-22 at 19 35 42

@mohamedsamehsalah mohamedsamehsalah added this to the 0.10.0 milestone Apr 23, 2023
@mohamedsamehsalah mohamedsamehsalah linked an issue Apr 23, 2023 that may be closed by this pull request
3 tasks
@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.

@@ -90,7 +91,8 @@ ImmutableSet<Boolean> testStreamIsNotEmpty() {
ImmutableSet<Optional<String>> testStreamMin() {
return ImmutableSet.of(
Stream.of("foo").max(comparingInt(String::length).reversed()),
Stream.of("bar").sorted(comparingInt(String::length)).findFirst());
Stream.of("bar").sorted(comparingInt(String::length)).findFirst(),
Stream.of("baz").collect(java.util.stream.Collectors.minBy(comparingInt(String::length))));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure about this...
For some reason, test output expcts to have the same imports are input test 😕.

While running fmt:format removes the expected imported packages.

Copy link
Member

Choose a reason for hiding this comment

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

Yep, that's why we have that funny elidedTypesAndStaticImports method at the top :).

@BeforeTemplate
long before(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class) ToIntFunction<T> mapper) {
Copy link
Contributor Author

@mohamedsamehsalah mohamedsamehsalah Apr 23, 2023

Choose a reason for hiding this comment

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

I could not Generalise Function<? super T, Integer> mapper from StreamMapToIntSum....

Ditto StreamMapToDoubleSum & StreamMapToLongSum

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, so what we usually do here is to combine the @BeforeTemplates, even though some will have a parameter type that doesn't match the @AfterTemplate. In this case that's already true for the existing rule, so we can add the extra @BeforeTemplate to it. Two things to note:

  • None of the new rules should have @Matches(IsLambdaExpressionOrMethodReference.class). That matcher is only present on the existing rules precisely because the parameter type mismatch :)
  • The compiler may want about a potential ambiguity between the two before methods; this we can avoid using a rename.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you so much for the clarification. 🚀

One question though
should'nt

....Function<? super T, Integer> mapper) {
  return stream.collect(summingInt(t -> mapper.apply(t).intValue()));
}

be equal to

....ToIntFunction<T> mapper) {
 return stream.collect(summingInt(mapper));
}

The tests fail but the compiler aint complaining 😓 ...

Copy link
Member

Choose a reason for hiding this comment

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

Explained offline 😄

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.

Thanks @mohamedsamehsalah! I added a commit.

We have some unwritten fuzzy rule about the order in which we place Refaster rules inside the enclosing class, based on which some of these new rules should perhaps be moved up. But since eventually we'll likely sort the rules lexicographically (or according to some other more objective algorithm), I'm fine with merging this as-is.

Suggested commit message:

Add additional Refaster rules to `StreamRules` (#593)

All changes suggested by SonarCloud's java:S4266 rule are now covered.

See https://sonarcloud.io/organizations/picnic-technologies/rules?open=java%3AS4266&rule_key=java%3AS4266

Fixes #578.

@BeforeTemplate
long before(
Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class) ToIntFunction<T> mapper) {
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, so what we usually do here is to combine the @BeforeTemplates, even though some will have a parameter type that doesn't match the @AfterTemplate. In this case that's already true for the existing rule, so we can add the extra @BeforeTemplate to it. Two things to note:

  • None of the new rules should have @Matches(IsLambdaExpressionOrMethodReference.class). That matcher is only present on the existing rules precisely because the parameter type mismatch :)
  • The compiler may want about a potential ambiguity between the two before methods; this we can avoid using a rename.

Comment on lines 497 to 560
static final class StreamReduce<T> {
@BeforeTemplate
T before(Stream<T> stream, T identity, BinaryOperator<T> accumulator) {
return stream.collect(reducing(identity, accumulator));
}

@AfterTemplate
T after(Stream<T> stream, T identity, BinaryOperator<T> accumulator) {
return stream.reduce(identity, accumulator);
}
}

static final class StreamReduceOptional<T> {
@BeforeTemplate
Optional<T> before(Stream<T> stream, BinaryOperator<T> accumulator) {
return stream.collect(reducing(accumulator));
}

@AfterTemplate
Optional<T> after(Stream<T> stream, BinaryOperator<T> accumulator) {
return stream.reduce(accumulator);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I think that so far we used the shorter/simpler rule name for the shorter/simpler code. That would change the names of these rules to StreamReduceAccumulator and StreamReduceWithIdentity, respectively.

@@ -90,7 +91,8 @@ ImmutableSet<Boolean> testStreamIsNotEmpty() {
ImmutableSet<Optional<String>> testStreamMin() {
return ImmutableSet.of(
Stream.of("foo").max(comparingInt(String::length).reversed()),
Stream.of("bar").sorted(comparingInt(String::length)).findFirst());
Stream.of("bar").sorted(comparingInt(String::length)).findFirst(),
Stream.of("baz").collect(java.util.stream.Collectors.minBy(comparingInt(String::length))));
Copy link
Member

Choose a reason for hiding this comment

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

Yep, that's why we have that funny elidedTypesAndStaticImports method at the top :).

Stream<T> stream,
@Matches(IsLambdaExpressionOrMethodReference.class) Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> collector) {
return stream.collect(mapping(mapper, collector));
Copy link
Member

Choose a reason for hiding this comment

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

While missing from SonarCloud's overview, we can add similar rules for filtering, flatMapping and summarizing*.

}
}

static final class StreamMap<T, U, A, R> {
Copy link
Member

Choose a reason for hiding this comment

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

Our naming strategy would call for StreamMapCollect.

@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.

@Stephan202 Stephan202 force-pushed the 578-refaster-rule-that-replaces-redundant-streamcollect-calls branch from 78e60cf to b8b6adc Compare April 25, 2023 06:34
@Stephan202 Stephan202 changed the title 578-refaster-rule-that-replaces-redundant-streamcollect-calls Add additional Refaster rules to StreamRules Apr 25, 2023
@Stephan202
Copy link
Member

Rebased and resolved trivial conflicts.

@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.

@Stephan202
Copy link
Member

^ New SonarCloud violations. I'll have a look.

@Stephan202 Stephan202 force-pushed the 578-refaster-rule-that-replaces-redundant-streamcollect-calls branch from b8b6adc to 653403b Compare April 25, 2023 09:38
@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.

Added two commits. @rickie I think this one is ready for your eyes :)

@@ -263,9 +280,12 @@ boolean after(Stream<T> stream) {

static final class StreamMin<T> {
@BeforeTemplate
@SuppressWarnings("java:S1155" /* This violation will be rewritten. */)
Copy link
Member

Choose a reason for hiding this comment

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

Here and below: we could introduce a separate method to further reduce this suppression's scope, but not sure that this is worth it.

Copy link
Member

Choose a reason for hiding this comment

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

Don't think that is worth it indeed.

@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.

1 similar comment
@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

@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.

Some questions and considerations :).

In general, looks good! Curious to see the impact 😄.

@@ -263,9 +280,12 @@ boolean after(Stream<T> stream) {

static final class StreamMin<T> {
@BeforeTemplate
@SuppressWarnings("java:S1155" /* This violation will be rewritten. */)
Copy link
Member

Choose a reason for hiding this comment

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

Don't think that is worth it indeed.

@@ -428,4 +469,130 @@ long after(Stream<T> stream, ToLongFunction<T> mapper) {
return stream.mapToLong(mapper).sum();
}
}

static final class StreamMapToIntSummaryStatistics<T> {
Copy link
Member

Choose a reason for hiding this comment

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

I'd suspect *SummaryStatistics rules to be at the end as they are not directly on Stream 🤔.

So the ordering is not lexicographical but also not based on the order of Stream itself 🤔. filter, map and flatMap things are first in that class.

Copy link
Member

Choose a reason for hiding this comment

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

If you want to reorder things based on a formula that we can eventually automate/auto-enforce: go for it 😄

}
}

static final class StreamFlatMapCollect<T, U, R> {
Copy link
Member

Choose a reason for hiding this comment

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

As Stream#collect and Stream#reduce both have two (or three) overloads, we could consider to add an extra type suffix, but we can also defer till at some point we have the name generator 😋.

Copy link
Member

Choose a reason for hiding this comment

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

Up to you 🙃

@rickie
Copy link
Member

rickie commented Apr 25, 2023

Well, okay let's leave it for now. Just wanted to make sure we are aware of the choice we were implicitly making 😄.

@rickie
Copy link
Member

rickie commented Apr 25, 2023

Pinged @werli on Slack and I made some suggestions to the commit message from the PR description.

Copy link
Member

@werli werli left a comment

Choose a reason for hiding this comment

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

Nothing to add from my side after @Stephan202 & @rickie's suggestions. 👍

Thx @mohamedsamehsalah 💪

mohamedsamehsalah and others added 4 commits April 26, 2023 08:05
@rickie rickie force-pushed the 578-refaster-rule-that-replaces-redundant-streamcollect-calls branch from 9b6a0e6 to c826335 Compare April 26, 2023 06:05
@rickie
Copy link
Member

rickie commented Apr 26, 2023

Rebased, will merge once 🟢 .

@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.

@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 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@rickie rickie changed the title Add additional Refaster rules to StreamRules Extend StreamRules Refaster rule collection Apr 26, 2023
@rickie rickie merged commit 32d50ab into master Apr 26, 2023
@rickie rickie deleted the 578-refaster-rule-that-replaces-redundant-streamcollect-calls branch April 26, 2023 07:07
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.

Refaster rule that replaces redundant Stream#collect(..) calls
4 participants