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

Model Map.getOrDefault #724

Merged
merged 13 commits into from
Feb 1, 2023
Merged

Model Map.getOrDefault #724

merged 13 commits into from
Feb 1, 2023

Conversation

msridhar
Copy link
Collaborator

@msridhar msridhar commented Jan 27, 2023

We model the method so that the nullability of the return matches the nullability of the second argument. We also add an explicit model of ImmutableMap.getOrDefault, to override the annotations on that method for the case where AcknowledgeRestrictiveAnnotations is set.

Fixes #723

@msridhar msridhar changed the title [WIP] model Map.getOrDefault Model Map.getOrDefault Jan 30, 2023
@msridhar msridhar changed the title Model Map.getOrDefault Model Map.getOrDefault Jan 30, 2023
@msridhar msridhar marked this pull request as ready for review January 30, 2023 23:54
@coveralls
Copy link

coveralls commented Jan 30, 2023

Pull Request Test Coverage Report for Build #1056

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 4 unchanged lines in 2 files lost coverage.
  • Overall coverage decreased (-0.01%) to 93.044%

Files with Coverage Reduction New Missed Lines %
../nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPath.java 1 95.24%
../nullaway/src/main/java/com/uber/nullaway/LibraryModels.java 3 88.0%
Totals Coverage Status
Change from base Build #1055: -0.01%
Covered Lines: 5297
Relevant Lines: 5693

💛 - Coveralls

Comment on lines +619 to +621
methodRef(
"com.google.common.collect.ImmutableMap", "getOrDefault(java.lang.Object,V)"),
1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ideally this would be inherited always and we wouldn't need to list it explicitly here. Unfortunately, for the combination of an inherited model and AcknowledgeRestrictiveAnnotations this does not seem to happen. Rather than trying to fix that subtle corner case, I decided to just list the method explicitly.

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, in retrospect, that it makes some amount of sense that the resolution order is (for B extends A):

  • Explicit library models for overriding method B.foo(Object o)
  • Restrictive annotations in the jar for overriding method B.foo(Object o)
  • Explicit library models for overridden method A.foo(Object o)
  • Restrictive annotations in the jar for overridden method A.foo(Object o)

We just want to make sure that library models for a given method override near everything else for that exact method.

That said, this case is still weird, because I doubt ImmutableMap has @Contract(...) and we are already assuming all arguments and the return are @Nullable in the more general case, no? I guess my model is a bit flawed here in that adding a library model of NULL_IMPLIES_NULL_PARAMETERS is not actually the same as annotating with @Contract(...). For example, I would have expected us to also need to assert that Map::get can return @Nullable explicitly in a different library model, but adding the method here actually implies that, whereas adding an @Contract wouldn't have.

Either way, happy with this explicit fix for now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That said, this case is still weird, because I doubt ImmutableMap has @Contract(...) and we are already assuming all arguments and the return are @Nullable in the more general case, no? I guess my model is a bit flawed here in that adding a library model of NULL_IMPLIES_NULL_PARAMETERS is not actually the same as annotating with @Contract(...). For example, I would have expected us to also need to assert that Map::get can return @Nullable explicitly in a different library model, but adding the method here actually implies that, whereas adding an @Contract wouldn't have.

I think the issue here was that the documentation for com.uber.nullaway.LibraryModels#nullImpliesNullParameters was unclear. I tried to clarify here: https://github.com/uber/NullAway/pull/724/files#diff-a42b334be0392157034a866655e5e0596680448baad4ed205d09858d061b3a95R87 Does that help?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Appreciate the updated documentation! But, in the spirit of full transparency, I hadn't RTFM, I was just going with what I remember nullImpliesNullParameters meaning, that my mistaken recollections matched our mistaken docs is purely coincidental 😅

@msridhar
Copy link
Collaborator Author

Note that this change could lead to new warnings being reported; before, we treated Map.getOrDefault as unannotated, which meant it had a @NonNull return. Now, its return will be @Nullable in cases when the second parameter is `@Nullable, leading to newly-reported warnings.

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.

Very minor nits about naming in the test cases, and a long but ultimately no-op digression. Generally makes sense to me!

Comment on lines +619 to +621
methodRef(
"com.google.common.collect.ImmutableMap", "getOrDefault(java.lang.Object,V)"),
1)
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, in retrospect, that it makes some amount of sense that the resolution order is (for B extends A):

  • Explicit library models for overriding method B.foo(Object o)
  • Restrictive annotations in the jar for overriding method B.foo(Object o)
  • Explicit library models for overridden method A.foo(Object o)
  • Restrictive annotations in the jar for overridden method A.foo(Object o)

We just want to make sure that library models for a given method override near everything else for that exact method.

That said, this case is still weird, because I doubt ImmutableMap has @Contract(...) and we are already assuming all arguments and the return are @Nullable in the more general case, no? I guess my model is a bit flawed here in that adding a library model of NULL_IMPLIES_NULL_PARAMETERS is not actually the same as annotating with @Contract(...). For example, I would have expected us to also need to assert that Map::get can return @Nullable explicitly in a different library model, but adding the method here actually implies that, whereas adding an @Contract wouldn't have.

Either way, happy with this explicit fix for now.

"import com.google.common.collect.ImmutableMap;",
"import org.jspecify.annotations.Nullable;",
"class Test {",
" void testGetOrDefaultMap(Map<String, String> m, String string, @Nullable String nullableString) {",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Minor nit, but could we call the first string nonNullString or similar? If not, let's call it s. I know it's perfectly legal, but lower-case "string" just seems like a typo for a fraction of a second every time I read over this line:

m.getOrDefault(\"key\", string).toString();

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c27f3e3

Copy link
Contributor

@XN137 XN137 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 picking this up!

" m.getOrDefault(\"key\", nullableString).toString();",
" }",
"",
" void testGetOrDefaultImmutableMap(ImmutableMap<String, String> im, String string, @Nullable String nullableString) {",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: when i originally copied the example from somewhere there was 3 space indentation on the 1st level, but maybe we want to use 2 instead everywhere

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks! Fixed in 9793733

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.

LGTM!

Feel free to land. As discussed, we are having this miss the train for NullAway 0.10.9 (which should be up soon and was cut already) on purpose, given the potential for many new reports on codebases using Map::getOrDefault, but we should cut a new release with this change (and a few others) soon.

Comment on lines +619 to +621
methodRef(
"com.google.common.collect.ImmutableMap", "getOrDefault(java.lang.Object,V)"),
1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Appreciate the updated documentation! But, in the spirit of full transparency, I hadn't RTFM, I was just going with what I remember nullImpliesNullParameters meaning, that my mistaken recollections matched our mistaken docs is purely coincidental 😅

@msridhar msridhar enabled auto-merge (squash) February 1, 2023 19:56
@msridhar msridhar merged commit 8e948db into master Feb 1, 2023
@msridhar msridhar deleted the manu/model-mapordefault branch March 9, 2023 17:06
msridhar added a commit to msridhar/NullAway that referenced this pull request Jul 18, 2023
msridhar added a commit to msridhar/NullAway that referenced this pull request Jul 18, 2023
msridhar added a commit to msridhar/NullAway that referenced this pull request Jul 19, 2023
msridhar added a commit to msridhar/NullAway that referenced this pull request Jul 19, 2023
msridhar added a commit to msridhar/NullAway that referenced this pull request Jul 19, 2023
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.

Better support for Map.getOrDefault
4 participants