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
Custom path to Optional class for Optional emptiness handler #288
Conversation
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.
See comments. Mostly: missing support for multiple optional types, and Config
should be passed to hanlders at construction, not on every interposition point.
@@ -70,6 +70,8 @@ | |||
|
|||
protected boolean checkOptionalEmptiness; | |||
|
|||
protected String optionalClassPath; |
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.
Should probably be an ImmutableSet<String>
, like with unannotatedClasses
. It should also be @Nullable
(for the case where checkOptionalEmptiness
is false
). We want to support the case of: a) No Optional classes passed, b) One Optional class passed (or taken from the default), c) N Optional classes passed. This field would only support (b)
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.
Done 👍
ClassTree tree, | ||
VisitorState state, | ||
Symbol.ClassSymbol classSymbol, | ||
Config config) { |
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.
Don't pass this here, pass it to the handler's constructor in Handlers.buildDefault(...)
, that's the convention used by existing handlers.
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.
Done 👍
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.
Before I go ahead and do a full pass, it looks to me like this remains to be addressed :)
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.
Done now 😅
if (optionalType == null) { | ||
optionalType = | ||
Optional.ofNullable(state.getTypeFromString(OPTIONAL_PATH)) | ||
Optional.ofNullable(state.getTypeFromString(config.getOptionalClassPath())) |
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.
Must be generalized to deal with multiple Optional
classes.
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.
Done 👍
"-XepOpt:NullAway:AnnotatedPackages=com.uber", | ||
"-XepOpt:NullAway:UnannotatedSubPackages=com.uber.lib.unannotated", | ||
"-XepOpt:NullAway:CheckOptionalEmptiness=true", | ||
"-XepOpt:NullAway:OptionalClassPath=com.google.common.base.Optional")) |
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.
👍 Would suggest also:
- Testing for
"-XepOpt:NullAway:OptionalClassPath=\"\""
(I'd say this should just raise a config error ifXepOpt:NullAway:CheckOptionalEmptiness
is set) - Testing for
"-XepOpt:NullAway:OptionalClassPath=java.utils.Optional,com.google.common.base.Optional"
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.
Should we not have java.utils.Optional
by default in the set?
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 way I would do it, if possible:
- No option at all passed in the command line => set is (
java.utils.Optional
) - Explicit option passed, empty or unparsable => error
- Explicit option passed, parsable non-empty set => the classes in that set, and nothing else
This allows people using this handler for their own custom Optional
without enabling it for java.utils.Optional
.
Then again, not sure if it's more consistent with how we do other stuff to just have java.utils.Optional
always on (when CheckOptionalEmptiness
is set), then rename this option to something like CustomOptionalClasses
. Maybe on second thought, I am liking this later way of doing it better.
@msridhar , thoughts ?
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 we should always check java.util.Optional
and this option should add additional aliases for it.
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.
Okay then. So first two cases as Lazaro described and and in case 3. Explicit option passed, parsable non-empty set => the classes in that set + java.utils.Optional
.
That works @lazaroclapp?
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 am not sure if we really need the unparsable check. Since we do not have it for other configs. Also I'm not sure how to define it exactly. So let me know if you think it should be necessary.
@msridhar @lazaroclapp
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 given what Manu is saying, then there is just two cases:
- No option at all passed in the command line => set is (java.utils.Optional)
CustomOptionalClasses=a.b.c.A1;a.b.c.A2
=> set is (java.utils.Optional,a.b.c.A1,a.b.c.A2)
Still worth to check that it works with zero (no CustomOptionalClasses
option), one and two classes being passed.
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 test for zero, one and two custom paths case.
@@ -56,7 +56,7 @@ | |||
static final String FL_ACKNOWLEDGE_RESTRICTIVE = | |||
EP_FL_NAMESPACE + ":AcknowledgeRestrictiveAnnotations"; | |||
static final String FL_CHECK_OPTIONAL_EMPTINESS = EP_FL_NAMESPACE + ":CheckOptionalEmptiness"; | |||
static final String FL_OPTIONAL_CLASS_PACKAGE = EP_FL_NAMESPACE + ":OptionalClassPath"; | |||
static final String FL_OPTIONAL_CLASS_PATHS = EP_FL_NAMESPACE + ":OptionalClassPaths"; |
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 still think this should be CustomOptionalClasses
, otherwise I keep parsing it on my head as "Optional (class paths)" rather than "(Optional class) paths"
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 that my alternative is perfect, but... mmm... maybe CheckOptionalEmptinessCustomClasses
? A bit verbose, but at least ties the two options together.
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.
Changed it to CheckOptionalEmptinessCustomClasses
, sounds 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.
Looking much better to me now, still one more test case I'd like to see and I am not sure why we are using Set<Optional<Type>>
rather than ImmutableSet<Type>
@@ -49,9 +51,12 @@ | |||
*/ | |||
public class OptionalEmptinessHandler extends BaseNoOpHandler { | |||
|
|||
private static String OPTIONAL_PATH = "java.util.Optional"; | |||
@Nullable private Set<Optional<Type>> optionalTypes; |
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.
Why is this a Set<Optional<Type>>
rather than a Set<Type>
? We could just filter out any type paths for which state.getTypeFromString(type)
returns null
.
Also, it probably should be an ImmutableSet
, since it won't change after the call to onMatchTopLevelClass
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.
Done 👍
"-XepOpt:NullAway:AnnotatedPackages=com.uber", | ||
"-XepOpt:NullAway:UnannotatedSubPackages=com.uber.lib.unannotated", | ||
"-XepOpt:NullAway:CheckOptionalEmptiness=true", | ||
"-XepOpt:NullAway:CheckOptionalEmptinessCustomClasses=com.google.common.base.Optional")) |
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.
Please add one more (smaller) test, that checks that java.util.Optional
is still recognized when this argument is set, as per our discussion. Tests are a good way to document that sort of design edge-case, after all.
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.
Done already 👍
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.
(True, I missed this one, my bad 😅 )
.map(state::getTypeFromString) | ||
.filter(Objects::nonNull) | ||
.map(state.getTypes()::erasure) | ||
.collect(Collectors.toSet())); |
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.
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.
So no need to build a Java set and call copyOf(...)
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.
Thanks! Done 👍
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.
Extremely minor nit, but overall it LGTM :)
Allowing custom path to the Optional class with
-XepOpt:NullAway: CheckOptionalEmptinessCustomClasses =
flag.This custom path is used by optional emptiness handler.
No option at all passed in the command line => set is (java.utils.Optional)
Explicit option passed => the classes in that set +
java.utils.Optional
Added unit test