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 Guava Preconditions Refaster rules #292

Merged
merged 8 commits into from
Oct 20, 2022
Merged

Conversation

werli
Copy link
Member

@werli werli commented Oct 12, 2022

This set of Guava Preconditions Refaster rules is non-exhaustive, but covers the most common use case for rewrites to Preconditions#check{Argument,State}().

I had pointed these four use cases out during code review multiple times by now, so adding a rule already. 👍

These templates only affect simple if constructs, not if-else. I think this should be part of another set of checks that removes the else-branch when the if-branch's responsibility is only throwing an exception (or returning something).

@werli werli requested a review from rickie October 12, 2022 09: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.

Very nice! Pushed a commit with some tweaks in line with "how we do it elsewhere", particularly in terms of naming.

The format string variants require special handling and are perhaps better deferred to another ticket (though I have some ideas around that; can write some stuff about that after the meetings I have now).

I would suggest also adding the other simple cases, though; don't mind doing those later :)

Comment on lines +34 to +44
@BeforeTemplate
void before(boolean condition, String message) {
if (condition) {
throw new IllegalArgumentException(message);
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Opening a thread w/ @Stephan202's message:

The format string variants require special handling and are perhaps better deferred to another ticket (though I have some ideas around that; can write some stuff about that after the meetings I have now).

I also thought of leaving many of those as follow up. 👍

One thought on this however. Wouldn't e.g. a String#format() call already partially be handled by the case I had pointed out here? As a result, we'd get

checkArgument(!condition, String.format(message, obj1));

which I'd then expect to be re-written by another Preconditions rule. Curious to hear the thinking.

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 philosophy of the Refaster rules defined in this repository are that they define the smallest/most generic possible improvement, so that in unison they improve the largest amount of code. So what you propose is the way to go.

There's one caveat here: not all String.format calls can be replaced. What I suspect we need is a Matcher that identifies valid Strings.lenientFormat format strings; then we can instruct Refaster to rewrite only eligible expressions using @Matches(IsLenientFormatTemplateString.class). (Best name TBD.)

Copy link
Member

Choose a reason for hiding this comment

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

^ It'll be more "scalable" to handle the String.format logic as a BugChecker, so let's defer that to a separate PR.

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 another commit.

Comment on lines +34 to +44
@BeforeTemplate
void before(boolean condition, String message) {
if (condition) {
throw new IllegalArgumentException(message);
}
}
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 philosophy of the Refaster rules defined in this repository are that they define the smallest/most generic possible improvement, so that in unison they improve the largest amount of code. So what you propose is the way to go.

There's one caveat here: not all String.format calls can be replaced. What I suspect we need is a Matcher that identifies valid Strings.lenientFormat format strings; then we can instruct Refaster to rewrite only eligible expressions using @Matches(IsLenientFormatTemplateString.class). (Best name TBD.)

@werli
Copy link
Member Author

werli commented Oct 12, 2022

Latest commits LGTM @Stephan202, thanks 👍

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 one more commit.

Suggested commit message:

Introduce Guava `Preconditions` Refaster rules (#292)

Comment on lines +34 to +44
@BeforeTemplate
void before(boolean condition, String message) {
if (condition) {
throw new IllegalArgumentException(message);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

^ It'll be more "scalable" to handle the String.format logic as a BugChecker, so let's defer that to a separate PR.

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 :)

import com.google.errorprone.refaster.annotation.UseImportPolicy;

/** Refaster templates related to statements dealing with {@link Preconditions}. */
final class PreconditionsRules {
Copy link
Member

Choose a reason for hiding this comment

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

One more thing:

Suggested change
final class PreconditionsRules {
@OnlineDocumentation
final class PreconditionsRules {

@Stephan202 Stephan202 added this to the 0.5.0 milestone Oct 15, 2022
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.

Added a commit.

Nice improvements @werli 🚀 !

Suggested commit message LGTM, will merge soon.

@@ -41,6 +41,10 @@ int testCheckIndex() {
return Objects.checkIndex(0, 1);
}

void testCheckIndexConditional() {
Objects.checkIndex(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.

I think we should add a ImportPolicy for CheckIndex and CheckIndexConditional as well.

  • We do it for the PreconditionsRules as well.
  • We internally always prefer that as well.
  • Even the StaticImport enforces this. However, as that is quite an opinionated check, some people will probably disable this check. Therefore it would be nice to make sure we use the static import for this as well.

@rickie rickie merged commit 68dca32 into master Oct 20, 2022
@rickie rickie deleted the werli/preconditions branch October 20, 2022 06:55
rickie pushed a commit that referenced this pull request Oct 20, 2022
@JnRouvignac
Copy link

Did you consider Java 9's java.util.Objects methods checkFromIndexSize(), checkFromToIndex() and checkIndex()?

@rickie
Copy link
Member

rickie commented Oct 20, 2022

Hello @JnRouvignac, yes for checkIndex() we have two rules in AssortedRules.java.

Up until now we haven't yet considered checkFromIndexSize or checkFromIndex. Adding rules for that could be interesting though. Are you willing to file a ticket for that? If you could provide some examples there as well, we can discuss adding such a rule. Maybe you would be up for opening a PR for that 😄?

@Stephan202
Copy link
Member

Something I just realized (and should have considered earlier): in a small number of cases the rules introduced here can cause functional or performance issues. See google/guava#5927 for an example of the former. An example of the latter would be a positional argument that is expensive to compute; such a computation now happens eagerly/unconditionally.

It might be good to test these changes internally before the next tag. (There's no nice way to add a local @SuppressWarnings for this case; perhaps we should look into a comment-based do-not-replace marker 🤔.)

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.

4 participants