-
Notifications
You must be signed in to change notification settings - Fork 39
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 PatternRules
Refaster rule collection
#771
Conversation
Looks good. No mutations were possible for these changes. |
if (expectedSeverities.isEmpty()) { | ||
compilationTestHelper.doTest(); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously this wasn't necessary, because the expectErrorMessage
declarations would trigger a "I didn't see anything" AssertionError
, which would then cause the satisfies
clause to succeed. But that's obscure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only one suggestion. Will approve nonetheless. Feel free when the second approval is in 😉.
@@ -155,6 +155,7 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa | |||
@VisibleForTesting | |||
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_EXEMPTED_MEMBERS = | |||
ImmutableSetMultimap.<String, String>builder() | |||
.put("com.google.common.base.Predicates", "contains") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue that adding contains
and empty
to STATIC_IMPORT_EXEMPTED_IDENTIFIERS
would maybe be even better :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting suggestion. Perhaps yes, though (hypothetical?) static contains
/empty
methods that deal with "regular" collection membership would not necessarily be problematic when imported statically. (The currently exempted methods instead return predicates with custom semantics.)
So... let's revisit if/when we add more such exclusions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deal, let's do that :).
There was a problem hiding this 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!
@@ -155,6 +155,7 @@ public final class StaticImport extends BugChecker implements MemberSelectTreeMa | |||
@VisibleForTesting | |||
static final ImmutableSetMultimap<String, String> STATIC_IMPORT_EXEMPTED_MEMBERS = | |||
ImmutableSetMultimap.<String, String>builder() | |||
.put("com.google.common.base.Predicates", "contains") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting suggestion. Perhaps yes, though (hypothetical?) static contains
/empty
methods that deal with "regular" collection membership would not necessarily be problematic when imported statically. (The currently exempted methods instead return predicates with custom semantics.)
So... let's revisit if/when we add more such exclusions?
Rebased. |
8583a5e
to
55c299a
Compare
Kudos, SonarCloud Quality Gate passed! |
Looks good. No mutations were possible for these changes. |
|
||
@AfterTemplate | ||
Predicate<String> after(String pattern) { | ||
return Pattern.compile(pattern).asPredicate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO the Guava static builder is a nice short alternative; not sure I see the advantage in standardizing here (they also do not recommend themselves to not use it like with some other utilities we don't rewrite like Lists.transform
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see how this suggestion is slightly controversial, but my thinking is that, besides being a JDK alternative:
- This code makes explicit that the operation involves pattern compilation; something one will generally want to move to a static field whenever possible.
- Or, as in the cleaned-up test code, avoid altogether by simply calling
String#contains
instead.
- Or, as in the cleaned-up test code, avoid altogether by simply calling
- This code better conveys (through the explicit
Pattern
reference) that the predicate performs regex matching, rather than any other kind of pattern matching.
W.r.t. List#transform
: indeed we don't rewrite that variant, but that's primarily because realistically it can only be rewritten when considering the surrounding context. That method's documentation does suggest that Java 8+ users should prefer Stream#map
.
error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/PatternRules.java
Show resolved
Hide resolved
|
||
@AfterTemplate | ||
Predicate<String> after(Pattern pattern) { | ||
return pattern.asPredicate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also here I'd personally argue that the Guava variant is more descriptive (nor do they recommend against using it)
I'm also not sure it's 100% equivalent as JDK uses String
vs. Guava's CharSequence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also here I'd personally argue that the Guava variant is more descriptive (nor do they recommend against using it)
On descriptiveness I guess we'll just have to disagree 😅. W.r.t. recommendations: while contains
itself doesn't call it out, the returned com.google.common.base.Predicate
is described as a legacy type that should be avoided whenever possible.
I'm also not sure it's 100% equivalent as JDK uses
String
vs. Guava'sCharSequence
Indeed it isn't; this is a Refaster limitation that unfortunately more of our rules suffer from. (Longer-term I plan to automatically flag and annotate such rules such that they can e.g. be skipped.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the returned com.google.common.base.Predicate is described as a legacy type that should be avoided whenever possible.
good point, should that be 'attacked' even more generically? essentially the entire Predicates
class shouldn't be used then 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally yes! git grep com.google.common.base.Predicate -- '*.java'
shows that internally not too many usages remain, so it's doable. But in some cases the result may be passed to a com.google.common.base.Predicate
-expecting method, in which case a Refaster approach isn't great. That's the same issue as we have here, but I expect that the impact will be larger when replacing or
and not
. So propose to defer that to a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partially the discussion may be moot; a git grep 'Predicates.contains'
against the Picnic code base yields only the Error Prone Support code modified in this PR 😄
error-prone-contrib/src/main/java/tech/picnic/errorprone/refasterrules/PatternRules.java
Show resolved
Hide resolved
|
||
@AfterTemplate | ||
Predicate<String> after(String pattern) { | ||
return Pattern.compile(pattern).asPredicate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see how this suggestion is slightly controversial, but my thinking is that, besides being a JDK alternative:
- This code makes explicit that the operation involves pattern compilation; something one will generally want to move to a static field whenever possible.
- Or, as in the cleaned-up test code, avoid altogether by simply calling
String#contains
instead.
- Or, as in the cleaned-up test code, avoid altogether by simply calling
- This code better conveys (through the explicit
Pattern
reference) that the predicate performs regex matching, rather than any other kind of pattern matching.
W.r.t. List#transform
: indeed we don't rewrite that variant, but that's primarily because realistically it can only be rewritten when considering the surrounding context. That method's documentation does suggest that Java 8+ users should prefer Stream#map
.
|
||
@AfterTemplate | ||
Predicate<String> after(Pattern pattern) { | ||
return pattern.asPredicate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also here I'd personally argue that the Guava variant is more descriptive (nor do they recommend against using it)
On descriptiveness I guess we'll just have to disagree 😅. W.r.t. recommendations: while contains
itself doesn't call it out, the returned com.google.common.base.Predicate
is described as a legacy type that should be avoided whenever possible.
I'm also not sure it's 100% equivalent as JDK uses
String
vs. Guava'sCharSequence
Indeed it isn't; this is a Refaster limitation that unfortunately more of our rules suffer from. (Longer-term I plan to automatically flag and annotate such rules such that they can e.g. be skipped.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool stuff!
Left just one question about the testing.
|
||
/** Refaster rules related to code dealing with regular expressions. */ | ||
@OnlineDocumentation | ||
final class PatternRules { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noticed that only this rule is not in RefasterRulesTest#RULE_COLLECTIONS
, shouldn't it be included?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That means that this one is not actually being tested, so we definitely should add it.
Now that I think of it, we forgot that part for the #811 PR 🤯, will open a PR right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch; will fix! (And indeed, cue the "we should automate this" message 😅)
and( | ||
containsPattern("SuppressWarnings"), | ||
containsPattern("toImmutableList"), | ||
containsPattern("toList"))) | ||
m -> Stream.of("SuppressWarnings", "toImmutableList", "toList").allMatch(m::contains)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking aloud, not in this PR, but I was wondering whether utility functions that return non pattern based predicates would be useful. In this case it wouldn't hide any potentially expensive underlying operation.
In my very personal opinion contains("foo")
is much more nicer to read than str -> str.contains("foo")
.
Seeing that we don't have Predicates#contains
in Picnic codebase, I myself questioning the usefulness of these utilities, but maybe only for eps? Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I would say that such a utility method has an insufficient utility times ubiquity (or power-to-weight) ratio. Separately from that, when I see str -> str.contains("foo")
I unambiguously know what that code will do, while with contains("foo")
I'm like "that's funny, a helper method for such a simple operation; let me check the implementation to see whether it does anything special". So all-in-all I favour the current approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tnx for all the careful reviews! ❤️
|
||
/** Refaster rules related to code dealing with regular expressions. */ | ||
@OnlineDocumentation | ||
final class PatternRules { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch; will fix! (And indeed, cue the "we should automate this" message 😅)
|
||
@AfterTemplate | ||
Predicate<String> after(Pattern pattern) { | ||
return pattern.asPredicate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally yes! git grep com.google.common.base.Predicate -- '*.java'
shows that internally not too many usages remain, so it's doable. But in some cases the result may be passed to a com.google.common.base.Predicate
-expecting method, in which case a Refaster approach isn't great. That's the same issue as we have here, but I expect that the impact will be larger when replacing or
and not
. So propose to defer that to a separate PR.
and( | ||
containsPattern("SuppressWarnings"), | ||
containsPattern("toImmutableList"), | ||
containsPattern("toList"))) | ||
m -> Stream.of("SuppressWarnings", "toImmutableList", "toList").allMatch(m::contains)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I would say that such a utility method has an insufficient utility times ubiquity (or power-to-weight) ratio. Separately from that, when I see str -> str.contains("foo")
I unambiguously know what that code will do, while with contains("foo")
I'm like "that's funny, a helper method for such a simple operation; let me check the implementation to see whether it does anything special". So all-in-all I favour the current approach.
55c299a
to
a16548c
Compare
Looks good. No mutations were possible for these changes. |
a16548c
to
b33c1d9
Compare
Looks good. No mutations were possible for these changes. |
b33c1d9
to
ec6a790
Compare
Rebased, will merge once 📗 , nice discussions 🚀 ! |
Looks good. No mutations were possible for these changes. |
While there, configure `StaticImport` to not require static importing of `com.google.common.base.Predicates.contains`. The new rules triggered cleanup of some `CompilationTestHelper` usages. See google/guava#6483.
ec6a790
to
45dafa8
Compare
Kudos, SonarCloud Quality Gate passed! |
Looks good. No mutations were possible for these changes. |
Suggested commit message: