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

Introduce OptionalOrElse check #1024

Merged
merged 4 commits into from
Mar 25, 2024
Merged

Conversation

Stephan202
Copy link
Member

@Stephan202 Stephan202 commented Feb 10, 2024

I found a branch from back in 2021; decided to dust it off :)

Suggested commit message:

Introduce `OptionalOrElse` check (#1024)

While there, extend the `OptionalIdentity` Refaster rule to
automatically resolve one class of `NestedOptionals` violations.

@Stephan202 Stephan202 added this to the 0.16.0 milestone Feb 10, 2024
Copy link

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

class surviving killed
🎉tech.picnic.errorprone.refaster.runner.Refaster 0 1
🎉tech.picnic.errorprone.bugpatterns.OptionalOrElse 0 34

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

@Stephan202 Stephan202 force-pushed the sschroevers/optional-improvements branch from cdced30 to 41e4d30 Compare February 11, 2024 15:17
Copy link

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

class surviving killed
🎉tech.picnic.errorprone.refaster.runner.Refaster 0 1
🎉tech.picnic.errorprone.bugpatterns.OptionalOrElse 0 34

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

@Stephan202 Stephan202 force-pushed the sschroevers/optional-improvements branch from 41e4d30 to 2cc1e26 Compare February 11, 2024 17:11
@Stephan202
Copy link
Member Author

Rebased and added a commit to utilize JDK 17 functionality.

Copy link

Copy link

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

class surviving killed
🎉tech.picnic.errorprone.refaster.runner.Refaster 0 1
🎉tech.picnic.errorprone.bugpatterns.OptionalOrElse 0 36

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

Copy link
Contributor

@mohamedsamehsalah mohamedsamehsalah left a comment

Choose a reason for hiding this comment

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

Sorry for slow review 🐢

I have some questions 🙌

@@ -442,9 +446,7 @@ Optional<? extends T> after(Optional<S> optional, Function<? super S, ? extends
static final class OptionalStream<T> {
@BeforeTemplate
Stream<T> before(Optional<T> optional) {
return Refaster.anyOf(
optional.map(Stream::of).orElse(Stream.empty()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to align my thoughts on this.

We are making an assumption that (the new) BugChecker will flag this, so no need for refaster rule, which makes sense 👍

(This is the part where I need help in)
However, doesn't this conflict with the assumption here that we don't rely on the existence of NestedOptional BugChecker ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question @mohamedsamehsalah. This is explained by a crucial difference between NestedOptionals and OptionalOrElse: the latter has a SuggestedFix (which then obviates the need for a corresponding Refaster rule), while the former only flags issues, but doesn't provide a fix (which means that there's still value in providing a Refaster rule for a subset of violations).

Comment on lines +91 to +101
" optional.orElse(new int[0].length);",
" }",
"",
" private <T> T foo() {",
" return null;",
" }",
"",
" private <S, T> T bar() {",
" return null;",
" }",
"}")
Copy link
Contributor

@mohamedsamehsalah mohamedsamehsalah Feb 16, 2024

Choose a reason for hiding this comment

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

If I do this

Suggested change
" optional.orElse(new int[0].length);",
" }",
"",
" private <T> T foo() {",
" return null;",
" }",
"",
" private <S, T> T bar() {",
" return null;",
" }",
"}")
" optional.orElse(new int[0].length);",
" optional.orElse(B.NUMBER);",
" optional.orElse(B.getNumber());",
" }",
"",
" private <T> T foo() {",
" return null;",
" }",
"",
" private <S, T> T bar() {",
" return null;",
" }",
"",
" class B {",
" static final int NUMBER = 1;",
"",
" static String getNumber() {",
" return \"number\";",
" }",
" }",
"}")

Shouldn't we expect optional.orElse(B.NUMBER) to be optional.orElseGet(() -> B.NUMBER) ?

This is what I get instead
Screenshot 2024-02-16 at 17 03 47

Copy link
Member Author

Choose a reason for hiding this comment

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

This is intended :). The idea here is that field dereferencing (foo.bar.baz etc.) is very efficient (conceptually: just some pointer dereferencing), so there's no point in deferring that work.

Copy link
Member Author

@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 reviewing @mohamedsamehsalah! 🙏

@@ -442,9 +446,7 @@ Optional<? extends T> after(Optional<S> optional, Function<? super S, ? extends
static final class OptionalStream<T> {
@BeforeTemplate
Stream<T> before(Optional<T> optional) {
return Refaster.anyOf(
optional.map(Stream::of).orElse(Stream.empty()),
Copy link
Member Author

Choose a reason for hiding this comment

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

Good question @mohamedsamehsalah. This is explained by a crucial difference between NestedOptionals and OptionalOrElse: the latter has a SuggestedFix (which then obviates the need for a corresponding Refaster rule), while the former only flags issues, but doesn't provide a fix (which means that there's still value in providing a Refaster rule for a subset of violations).

Comment on lines +91 to +101
" optional.orElse(new int[0].length);",
" }",
"",
" private <T> T foo() {",
" return null;",
" }",
"",
" private <S, T> T bar() {",
" return null;",
" }",
"}")
Copy link
Member Author

Choose a reason for hiding this comment

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

This is intended :). The idea here is that field dereferencing (foo.bar.baz etc.) is very efficient (conceptually: just some pointer dereferencing), so there's no point in deferring that work.

Copy link
Contributor

@mohamedsamehsalah mohamedsamehsalah left a comment

Choose a reason for hiding this comment

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

Thanks for answering my questions @Stephan202 🚀

@rickie rickie modified the milestones: 0.16.0, 0.17.0 Mar 8, 2024
@Stephan202 Stephan202 force-pushed the sschroevers/optional-improvements branch from 2cc1e26 to 4ada3b2 Compare March 17, 2024 10:00
@rickie
Copy link
Member

rickie commented Mar 23, 2024

/integration-test

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.

Slightly tweaked suggested commit message.

Nice PR 🚀 !

Curious to see if it has an impact on the Checkstyle integration test, so added a comment.

@@ -386,9 +386,13 @@ Optional<T> after(Optional<T> optional1, Optional<T> optional2) {
*/
static final class OptionalIdentity<T> {
@BeforeTemplate
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */)
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
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */)
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` violation. */)

Not wrong, but this is an alternative.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I'm not feeling this suggestion, TBH. Elsewhere we use /* This violation will be rewritten. */, perhaps that would work? (If so, we should do the same for OptionalOrOtherOptional.)

Copy link
Member Author

Choose a reason for hiding this comment

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

^ So far /* This violation will be rewritten. */ is used only for SonarCloud violations. In e.g ReactorRules and TimeRules I see that NestedPublishers and TimeZoneUsage violations are suppressed with out a comment. Likely we should add some text in each case, but that's for another PR, then. Will drop it here.

Copy link
Member Author

@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 typo fix in the suggested commit message!

@@ -386,9 +386,13 @@ Optional<T> after(Optional<T> optional1, Optional<T> optional2) {
*/
static final class OptionalIdentity<T> {
@BeforeTemplate
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */)
Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I'm not feeling this suggestion, TBH. Elsewhere we use /* This violation will be rewritten. */, perhaps that would work? (If so, we should do the same for OptionalOrOtherOptional.)

@Stephan202 Stephan202 force-pushed the sschroevers/optional-improvements branch from 4ada3b2 to 8326173 Compare March 23, 2024 22:04
@Stephan202
Copy link
Member Author

/integration-test

Copy link
Member Author

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

Comment on lines +53689 to +53697
@@ -1081,7 +1083,7 @@ public class XdocsPagesTest {
Optional.ofNullable(field)
.map(nonNullField -> nonNullField.getAnnotation(XdocsPropertyType.class))
.map(propertyType -> propertyType.value().getDescription())
- .orElse(fieldClass.getSimpleName());
+ .orElseGet(fieldClass::getSimpleName);
final String expectedValue =
getModulePropertyExpectedValue(sectionName, propertyName, field, fieldClass, instance);

Copy link
Member Author

Choose a reason for hiding this comment

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

With this change the integration test passes.

@@ -386,9 +386,13 @@ Optional<T> after(Optional<T> optional1, Optional<T> optional2) {
*/
static final class OptionalIdentity<T> {
@BeforeTemplate
@SuppressWarnings("NestedOptionals" /* Auto-fix for the `NestedOptionals` check. */)
Copy link
Member Author

Choose a reason for hiding this comment

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

^ So far /* This violation will be rewritten. */ is used only for SonarCloud violations. In e.g ReactorRules and TimeRules I see that NestedPublishers and TimeZoneUsage violations are suppressed with out a comment. Likely we should add some text in each case, but that's for another PR, then. Will drop it here.

Copy link

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

class surviving killed
🎉tech.picnic.errorprone.refaster.runner.Refaster 0 1
🎉tech.picnic.errorprone.bugpatterns.OptionalOrElse 0 36

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

While there, extend the `OptionalIdentity` Refaster rule to
automatically resolve once class of `NestedOptionals` violations.
@rickie rickie force-pushed the sschroevers/optional-improvements branch from b48264f to c05aa21 Compare March 25, 2024 08:15
@rickie
Copy link
Member

rickie commented Mar 25, 2024

Thanks for fixing the test and answering my questions :). Code LGTM, let's merge!

Copy link

Copy link

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

class surviving killed
🎉tech.picnic.errorprone.refaster.runner.Refaster 0 1
🎉tech.picnic.errorprone.bugpatterns.OptionalOrElse 0 36

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

@Stephan202 Stephan202 merged commit 281a003 into master Mar 25, 2024
15 checks passed
@Stephan202 Stephan202 deleted the sschroevers/optional-improvements branch March 25, 2024 08:29
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