-
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 a check to simplify time expressions in annotations #17
base: master
Are you sure you want to change the base?
Conversation
MethodSymbol argumentSymbol = | ||
(MethodSymbol) | ||
Iterables.getOnlyElement( | ||
scope.getSymbols(symbol -> symbol.getQualifiedName().contentEquals(argument))); | ||
return (VarSymbol) argumentSymbol.getDefaultValue().getValue(); |
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.
This is perhaps dodgy (and NullAway complains about #getDefaultValue
here, although the Iterables#getOnlyElement
could be just as problematic), but I assumed that the AnnotationDescriptor
is correct (and thus the attribute must exist). Maybe we should just return an Optional<VarSymbol>
instead, and short circuit in trySimplification
if we couldn't find a time unit for whatever reason. 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.
I think returning the Optional
with the short circuit is a clean solution and makes NullAway
happy :). So sounds good to me.
|
||
private static Fix getImplicitValueAttributeFix( | ||
AnnotationTree annotation, Number newValue, String timeUnitField, TimeUnit newTimeUnit) { | ||
@SuppressWarnings("TreeToString") |
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.
Because of annotation#toString
below, but SuggestedFixes#updateAnnotationArgumentValues
does this too. 🤔
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 pushed something that we can do specifically in Error Prone Support because Stephan added a Util.
} | ||
|
||
private static Optional<TimeSimplifier.Simplification> trySimplify(Number value, TimeUnit unit) { | ||
checkArgument( |
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.
Not sure how defensive we want to be here, or if we should prefer to return empty and avoid exceptions.
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'm also not sure, no preference here.
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.
This is a really nice BugPattern 🚀
tags = BugPattern.StandardTags.SIMPLIFICATION) | ||
public final class SimplifyTimeAnnotationCheck extends BugChecker implements AnnotationTreeMatcher { | ||
private static final long serialVersionUID = 1L; | ||
private static final AnnotationAttributeMatcher ARGUMENT_SELECTOR = getMatcher(); |
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.
We can use a more descriptive name here instead of getMatcher
. It is not directly clear what the matcher does.
private static final AnnotationAttributeMatcher ARGUMENT_SELECTOR = getMatcher(); | ||
|
||
@Override | ||
public Description matchAnnotation(AnnotationTree annotationTree, VisitorState visitorState) { |
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.
public Description matchAnnotation(AnnotationTree annotationTree, VisitorState visitorState) { | |
public Description matchAnnotation(AnnotationTree annotationTree, VisitorState state) { |
We typically use state
instead of visitorState
.
* in the given {@code unit}. | ||
*/ | ||
public long toUnit(TimeUnit unit) { | ||
return unit.convert(value, this.unit); |
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.
Because of the this.unit
I got a HidingField violation. Therefore renamed the variable.
private static Optional<TimeSimplifier.Simplification> trySimplify(Number value, TimeUnit unit) { | ||
checkArgument( | ||
value instanceof Integer || value instanceof Long, | ||
"Only time expressed as a long or integer can be simplified"); |
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.
Turned these two around to be in line with the order above this line.
|
||
private static Fix getImplicitValueAttributeFix( | ||
AnnotationTree annotation, Number newValue, String timeUnitField, TimeUnit newTimeUnit) { | ||
@SuppressWarnings("TreeToString") |
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 pushed something that we can do specifically in Error Prone Support because Stephan added a Util.
} | ||
|
||
private static Optional<TimeSimplifier.Simplification> trySimplify(Number value, TimeUnit unit) { | ||
checkArgument( |
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'm also not sure, no preference here.
MethodSymbol argumentSymbol = | ||
(MethodSymbol) | ||
Iterables.getOnlyElement( | ||
scope.getSymbols(symbol -> symbol.getQualifiedName().contentEquals(argument))); | ||
return (VarSymbol) argumentSymbol.getDefaultValue().getValue(); |
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 think returning the Optional
with the short circuit is a clean solution and makes NullAway
happy :). So sounds good to me.
.orElse(Description.NO_MATCH); | ||
} | ||
|
||
private static Optional<Fix> trySimplification( |
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.
trySimplification
in itself is not too descriptive. It makes sense because it is in this particular class, but I would prefer something like the name simplifyAttributeValue
which is used in the CanonicalAnnotationSyntaxCheck
.
Especially because this file has now a method with the name trySimplificiation
and 2 methods with trySimplify
. I know one is strictly speaking in another class (the TimeSimplifier
) but that's why I think changing the naming could make things a little more clear.
I want to remark the same for getValue
since it has two arguments which makes it harder to directly see what is going on there. Changing the name could help there.
Edit:
Hmm, I see that SpringMvcAnnotationCheck
does have the same setup as here.. So I get where you are coming from. Although I do think it would be an improvement 😄 .
import java.util.stream.Stream; | ||
|
||
/** | ||
* A {@link BugChecker} which flags methods annotated with annotations described by {@link |
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.
* A {@link BugChecker} which flags methods annotated with annotations described by {@link | |
* A {@link BugChecker} which flags methods annotated with annotations described by the {@link |
👀
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 don't think adding "the" is better. That said, will propose a more extensive rephrasing 👇
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.
Added a small commit and did a partial review. More later/another time :)
* order. | ||
*/ | ||
private static ImmutableSortedSet<TimeUnit> descendingLargerUnits(TimeUnit unit) { | ||
return ImmutableSortedSet.copyOf(TimeUnit.values()) |
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.
We can extract this one to a constant.
} | ||
|
||
/** Utility class to help simplify time expressions. */ | ||
private static final class TimeSimplifier { |
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.
This class name hints more at it being a type with instance methods. I think we can move the static methods to the Simplification
type.
/** | ||
* A {@link BugChecker} which flags methods annotated with annotations described by {@link | ||
* AnnotationDescriptor} that can be written more concisely. | ||
*/ |
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.
/** | |
* A {@link BugChecker} which flags methods annotated with annotations described by {@link | |
* AnnotationDescriptor} that can be written more concisely. | |
*/ | |
/** | |
* A {@link BugChecker} which flags annotations with time attributes that can be written more | |
* concisely. | |
*/ |
import java.util.stream.Stream; | ||
|
||
/** | ||
* A {@link BugChecker} which flags methods annotated with annotations described by {@link |
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 don't think adding "the" is better. That said, will propose a more extensive rephrasing 👇
Thx for the feedback guys! I'll try to circle back to this PR somewhere this week. :) |
8baa0c9
to
8ced020
Compare
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.
Rebased, resolved conflicts and added a commit. (Won't do a full review now, but saw that I had the branch checked out, and was wondering about its state.)
@@ -43,7 +42,7 @@ void identification() { | |||
" // BUG: Diagnostic contains:", | |||
" @RequestMapping(method = {DELETE}) A delete();", | |||
" // BUG: Diagnostic contains:", | |||
" @RequestMapping(method = {PATCH}) A patch();", | |||
" @RequestMapping(method = {org.springframework.web.bind.annotation.RequestMethod.PATCH}) A patch();", |
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.
👀 Unrelated change?
(Not a bad one, just surprising.)
I encountered the |
Yep, likely! Too bad it's all
I suppose that @nathankooij currently resides in the vicinity of a black hole ;) |
I wish to expand the test suite further, but it should already give a good indication of what is possible with this check.
A few things:
getTimeUnit
andgetDefaultTimeUnit
is always safe.- Returning a map of attributes -> expression (or assignment trees if that's always the tree) by the matcher;
- Extracting enum value from an expression;
- Extracting default attributes on annotations;
- An "annotation builder" to avoid special casing the implict
value
attribute.I know @Stephan202 mentioned he has some of these on a branch already, so curious to see what we can already do out of the box. :)