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 NestedOptionals check #202

Merged
merged 5 commits into from
Aug 17, 2022

Conversation

Venorcis
Copy link
Contributor

Based on my own suggestion and input in this Slack thread.

Disallows any (embedded) use of nested optionals.

@Venorcis Venorcis requested review from Stephan202 and rickie August 17, 2022 12:00
@Venorcis Venorcis force-pushed the vkoeman/optional-to-optional-mapping branch 2 times, most recently from b683883 to 9dab86c Compare August 17, 2022 15:26
@Stephan202 Stephan202 force-pushed the vkoeman/optional-to-optional-mapping branch from 9dab86c to 18f2dbf Compare August 17, 2022 18:37
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.

Rebased and added a commit. Very nice contribution!

Before I approve I'd like to check whether the MethodReferenceUsage violation can be resolved 👀

linkType = NONE,
severity = WARNING,
tags = SIMPLIFICATION)
public final class NestingOptionals extends BugChecker implements MethodInvocationTreeMatcher {
Copy link
Member

Choose a reason for hiding this comment

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

W.r.t. the name: WDYT of NestedOptionals?

Copy link
Member

Choose a reason for hiding this comment

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

Was doubting between NestedOptionals and NestedOptional. Looking at the existing checks, in most cases we currently use the singular form of writing.

I think the name NestedOptional would be an even better fit. WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

SGTM 👍

Copy link
Member

Choose a reason for hiding this comment

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

Actually, wait. I did consider that, but was worried that a Stream<Optional<T>> also covers that name. Hence the plural (i.e. "at least two Optionals).

Copy link
Member

Choose a reason for hiding this comment

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

Ah good one, didn't think of that case. Let's roll with this.

Comment on lines 50 to 64
private static class NestedOptionalDetector extends TreeScanner<Boolean, VisitorState> {
@Nullable
@Override
public Boolean visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
if (ASTHelpers.getType(node).getTypeArguments().stream()
.anyMatch(NestedOptionalDetector::typeIsJavaOptional)) {
return true;
}
return super.visitMethodInvocation(node, state);
}

private static boolean typeIsJavaOptional(Type type) {
return type.asElement().getQualifiedName().contentEquals(JAVA_OPTIONAL);
}
}
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 we can simplify this code and avoid the string comparison in the process (which should make things more efficient). Will push a proposal :)

@@ -156,6 +156,7 @@ private static Optional<SuggestedFix.Builder> constructMethodRef(
return constructFix(lambdaExpr, lhsType.tsym, subTree.getIdentifier());
}

@SuppressWarnings("NestingOptionals")
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, we're doing some dodgy stuff here 🤔

@@ -311,6 +311,7 @@ Optional<R> after(
/** Prefer {@link Optional#or(Supplier)} over more verbose alternatives. */
abstract static class OptionalOrOtherOptional<T> {
@BeforeTemplate
@SuppressWarnings("NestingOptionals")
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("NestingOptionals")
@SuppressWarnings("NestingOptionals" /* Auto-fix for the `NestingOptionals` check. */)

@Venorcis
Copy link
Contributor Author

Thanks @Stephan202, great improvements 🚀

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.

Nice check and congrats on your first contribution @Venorcis 🚀 !

@Stephan202 TBH I don't think it is worth the time now to fix the suppressions in MethodReferenceUsage. The check isn't enabled at the moment and we'll have to put quite some effort into it anyway before it will be.

Suggested commit message:

Introduce `NestedOptionals` check (#202)

" // BUG: Diagnostic contains:",
" Optional.of(Optional.empty());",
" // BUG: Diagnostic contains:",
" Optional.of(Optional.of(1));",
Copy link
Member

Choose a reason for hiding this comment

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

For readability we could add an empty line here, below the two ofNullable examples and before the Stream.of examples. Not a big win though.

We do this in some other tests as well.

"Avoid nesting `Optional`s inside `Optional`s; the resultant code is hard to reason about",
linkType = NONE,
severity = WARNING,
tags = SIMPLIFICATION)
Copy link
Member

Choose a reason for hiding this comment

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

We are not actually doing the simplification, we are flagging fragile code 😄.

Suggested change
tags = SIMPLIFICATION)
tags = FRAGILE_CODE)

Right?

linkType = NONE,
severity = WARNING,
tags = SIMPLIFICATION)
public final class NestingOptionals extends BugChecker implements MethodInvocationTreeMatcher {
Copy link
Member

Choose a reason for hiding this comment

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

Ah good one, didn't think of that case. Let's roll with this.

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.

I had hoped that the MethodReferenceUsage code on this branch didn't suffer from the same issue, but alas.

Added a comment to clarify that the current code is undesirable.

@Stephan202 Stephan202 merged commit 130c3d0 into master Aug 17, 2022
@Stephan202 Stephan202 deleted the vkoeman/optional-to-optional-mapping branch August 17, 2022 20:14
@Stephan202 Stephan202 added this to the 0.1.1 milestone Aug 20, 2022
@rickie rickie changed the title Bugpattern for disallowing Optional<Optional<...>> types Introduce NestedOptionals check Aug 23, 2022
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