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

Prevent NestedOptionals from throwing an NPE #412

Merged
merged 2 commits into from
Dec 16, 2022

Conversation

gtoison
Copy link
Contributor

@gtoison gtoison commented Dec 14, 2022

While testing version 0.6.0 I encountered a NullPointerException thrown by NestedOptinal when analysing this code (please don't judge, it's a bug sample!):

void m() {
    java.time.Duration.of(1, ChronoUnit.YEARS);
}

This PR adds a unit test reproducing the problem and a proposed fix.
Thanks a lot for providing this project!

The type returned by OPTIONAL_OF_OPTIONAL.get(state) is null for code
sample: Duration.of(1, ChronoUnit.YEARS)
@github-actions
Copy link

Looks good. All 5 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.bugpatterns.NestedOptionals 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

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.

Thanks for trying the new release and filing a bug fix @gtoison! Let us know if you find other issues (or have requests) :)

I added a commit with some suggestions.

Suggested commit message:

Prevent `NestedOptionals` from throwing an NPE (#412)

Previously, a `NullPointerException` was thrown if during compilation the
`java.util.Optional` class was not loaded at all.

(I suspect there may be similar such booby traps in other checks 🤔.

@gtoison let us know if you'd like to have a 0.6.1 patch release with this change. I might be able to make time for that in the next 1-2 days.

Comment on lines 44 to 49
Type stateType = OPTIONAL_OF_OPTIONAL.get(state);
if (stateType != null && state.getTypes().isSubtype(ASTHelpers.getType(tree), stateType)) {
return describeMatch(tree);
} else {
return Description.NO_MATCH;
}
Copy link
Member

Choose a reason for hiding this comment

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

Alright, so I guess that the issue happens if the Optional class isn't loaded during compilation at all. That realization also lead to this code.

Copy link
Member

Choose a reason for hiding this comment

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

We do have a preference for an early return with Description.NO_MATCH:

Suggested change
Type stateType = OPTIONAL_OF_OPTIONAL.get(state);
if (stateType != null && state.getTypes().isSubtype(ASTHelpers.getType(tree), stateType)) {
return describeMatch(tree);
} else {
return Description.NO_MATCH;
}
Type type = OPTIONAL_OF_OPTIONAL.get(state);
if (type == null || !state.getTypes().isSubtype(ASTHelpers.getType(tree), type)) {
return Description.NO_MATCH;
}
return describeMatch(tree);

"",
"class B {",
" void m() {",
" java.time.Duration.of(1, ChronoUnit.YEARS);",
Copy link
Member

Choose a reason for hiding this comment

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

I suggest using a slightly more realistic case ;)

jshell> java.time.Duration.of(1, java.time.temporal.ChronoUnit.YEARS)
|  Exception java.time.temporal.UnsupportedTemporalTypeException: Unit must not have an estimated duration
|        at Duration.plus (Duration.java:717)
|        at Duration.of (Duration.java:312)
|        at (#1:1)

@@ -39,4 +39,19 @@ void identification() {
"}")
.doTest();
}

@Test
void failingTypeCheck() {
Copy link
Member

Choose a reason for hiding this comment

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

Slightly more in line which what we do in a few other places

Suggested change
void failingTypeCheck() {
void identificationOptionalTypeNotLoaded() {

(Funny: I tried to replace the Duration expression below with Stream.empty(), but then the NPE didn't happen. I think because Stream does reference Optional, while Duration (and ChronoUnit) don't.

@Stephan202 Stephan202 added this to the 0.7.0 milestone Dec 14, 2022
@Stephan202 Stephan202 requested a review from rickie December 14, 2022 21:41
@github-actions
Copy link

Looks good. All 5 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.bugpatterns.NestedOptionals 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@gtoison
Copy link
Contributor Author

gtoison commented Dec 15, 2022

Thanks for the review and the fixes!
The integration tests I made only use very small bug samples. They check that some rules work as intented, so it's far from exhaustive.

A patch release would be nice but there's no need to rush things. I was planning to upgrade error-prone when there's a new release

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.

Thanks for opening a PR @gtoison 🚀 !

The changes look good to me 😄.
Also suggested commit message LGTM!

I'll rebase and merge once the build is green.

(Ah I don't need to rebase.)

@rickie
Copy link
Member

rickie commented Dec 16, 2022

Ah got one tweak for the suggested commit message @Stephan202 I think it should be "a NPE" instead of "an NPE" 👀?

@Stephan202
Copy link
Member

@rickie for acronyms it depends on pronunciation, and I personally literally say "en-pee-ee", so then "an" is correct. I think this is a gray area.

@rickie
Copy link
Member

rickie commented Dec 16, 2022

Aha, I don't do it like that. Anyway, let's roll with what is suggested then :).

@rickie rickie changed the title Fix for NPE thrown in NestedOptionals Prevent NestedOptionals from throwing an NPE Dec 16, 2022
@rickie rickie merged commit 870d16a into PicnicSupermarket:master Dec 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

3 participants