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

Custom path to Optional class for Optional emptiness handler #288

Merged
merged 5 commits into from Mar 22, 2019

Conversation

shubhamugare
Copy link
Contributor

@shubhamugare shubhamugare commented Mar 14, 2019

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

Copy link
Collaborator

@lazaroclapp lazaroclapp left a 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;
Copy link
Collaborator

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)

Copy link
Contributor Author

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) {
Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 👍

Copy link
Collaborator

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

Copy link
Contributor Author

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()))
Copy link
Collaborator

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.

Copy link
Contributor Author

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"))
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 Would suggest also:

  1. Testing for "-XepOpt:NullAway:OptionalClassPath=\"\"" (I'd say this should just raise a config error if XepOpt:NullAway:CheckOptionalEmptiness is set)
  2. Testing for "-XepOpt:NullAway:OptionalClassPath=java.utils.Optional,com.google.common.base.Optional"

Copy link
Contributor Author

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?

Copy link
Collaborator

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:

  1. No option at all passed in the command line => set is (java.utils.Optional)
  2. Explicit option passed, empty or unparsable => error
  3. 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 ?

Copy link
Collaborator

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

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

Copy link
Collaborator

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:

  1. No option at all passed in the command line => set is (java.utils.Optional)
  2. 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.

Copy link
Contributor Author

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";
Copy link
Collaborator

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"

Copy link
Collaborator

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.

Copy link
Contributor Author

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

Copy link
Collaborator

@lazaroclapp lazaroclapp left a 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;
Copy link
Collaborator

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

Copy link
Contributor Author

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"))
Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done already 👍

Copy link
Collaborator

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()));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator

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(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Done 👍

Copy link
Collaborator

@lazaroclapp lazaroclapp left a 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 :)

@lazaroclapp lazaroclapp merged commit 31a1842 into uber:master Mar 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants