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

Update handling of annotations on varargs argument #1025

Merged
merged 29 commits into from
Sep 1, 2024

Conversation

msridhar
Copy link
Collaborator

@msridhar msridhar commented Aug 24, 2024

See #708 (comment) for an overview, and #674 (comment) for all the details of how annotations on the varargs argument are now checked by NullAway. Reproduced from #674 (comment), here are the updated behaviors for different types of annotations on the varargs formal parameter (Update the table is slightly tweaked from the previous comment; this table now matches the current thinking / implementation):

Annotation Type Annotation Position Mode @Nullable varargs array @Nullable array elements @Nullable args at calls
declaration - standard
declaration - legacy
declaration - JSpecify
type use before ... standard
type use before ... legacy
type use before ... JSpecify
type use elements standard
type use elements legacy
type use elements JSpecify
type use both standard
type use both legacy
type use both JSpecify
both before ... standard
both before ... legacy
both before ... JSpecify
both elements standard
both elements legacy
both elements JSpecify
both both standard
both both legacy
both both JSpecify

"@Nullable varargs array" means that the varargs array is treated as @Nullable within the method body, and a @Nullable array can be passed at call sites. "@Nullable array elements" means that elements of the varargs array are treated as @Nullable within the method body (only relevant in JSpecify mode). And, "@Nullable args at calls" means that individual arguments passed in the varargs position at call sites may be @Nullable.

Fixes #674

Copy link

codecov bot commented Aug 24, 2024

Codecov Report

Attention: Patch coverage is 97.43590% with 1 line in your changes missing coverage. Please review.

Project coverage is 86.06%. Comparing base (0d500cc) to head (535af4f).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...away/src/main/java/com/uber/nullaway/NullAway.java 95.45% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1025      +/-   ##
============================================
+ Coverage     86.00%   86.06%   +0.06%     
- Complexity     2091     2110      +19     
============================================
  Files            83       83              
  Lines          6908     6932      +24     
  Branches       1331     1344      +13     
============================================
+ Hits           5941     5966      +25     
+ Misses          551      550       -1     
  Partials        416      416              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@msridhar msridhar marked this pull request as ready for review August 24, 2024 03:20
@msridhar msridhar requested a review from lazaroclapp August 24, 2024 03:23
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.

Ok, left comments/nits as I went along, including suggestions of comments/naming that make things more readable for me, but which you can ultimately accept or ignored as desired.

Overall this LGTM, a few questions inline, that can be addressed in follow ups and also:

Do we have any tests checking that when given a JSpecify annotation we always act as in JSpecify mode? I saw that in one of the discussion comments, but not the tests.

nullaway/src/main/java/com/uber/nullaway/NullAway.java Outdated Show resolved Hide resolved
Comment on lines -1788 to -1789
// This statement should be unreachable without assigning actual beforehand:
Preconditions.checkNotNull(actual);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is still the case, right? Is just that now NullAway can prove this statically?

Copy link
Collaborator Author

@msridhar msridhar Sep 1, 2024

Choose a reason for hiding this comment

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

Correct, javac's initialized variables check now proves it (along with the fact that we never assign null to the variable)

"public class Utilities {",
" public static String takesNullableVarargs(Object o, @Nullable Object... others) {",
" String s = o.toString() + \" \" + others.toString();",
" return s;",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we be checking in here that the elements are @Nullable? By adding the for loop and trying to deref `other? Or is the table saying that in JSpecify mode (and every mode other than legacy) we don't check declaration annotations (but acknowledge them for call sites)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I did add the test in bc46f2e. And I have now changed the logic so that we get an error, in 6b622c7. Great catch! My table didn't have this case as an error before, due to an attempt to make treatment of array types identical for regular arrays and varargs arrays. But there are already other cases where we don't have identical treatment. And the previous table made a @Nullable declaration annotation a no-op within the method body, which could be confusing. So this is now fixed, and I will update the table accordingly.

Comment on lines +412 to +413
" Object[] x = new Object[10];",
" takesVarargs(x, o);",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, why isn't this an error in JSpecify mode? Isn't the initialization here an array of 10 null references?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this is again due to unsound handling of array initializers, see previous comment

msridhar and others added 5 commits August 31, 2024 17:49
Copy link
Collaborator Author

@msridhar msridhar left a comment

Choose a reason for hiding this comment

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

Thanks a lot for the detailed review @lazaroclapp!

Comment on lines -1788 to -1789
// This statement should be unreachable without assigning actual beforehand:
Preconditions.checkNotNull(actual);
Copy link
Collaborator Author

@msridhar msridhar Sep 1, 2024

Choose a reason for hiding this comment

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

Correct, javac's initialized variables check now proves it (along with the fact that we never assign null to the variable)

" Utilities.takesNullableVarargs(o1, (java.lang.Object) null);",
" Object[] x = null;",
" // BUG: Diagnostic contains: passing @Nullable parameter 'x'",
" Utilities.takesNullableVarargs(o1, x);",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yup, added in 71301a3

" String s = o.toString() + \" \";",
" // BUG: Diagnostic contains: enhanced-for expression others is @Nullable",
" for (Object other : others) {",
" s += (other == null) ? \"(null) \" : other.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.

That reasoning only occurs in JSpecify mode. It is tested in JSpecifyVarargsTests#typeUseAndDeclarationOnBoth and in other methods in that class.

Comment on lines +403 to +404
" Object[] x = new Object[10];",
" takesVarargs(x, o);",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a separate issue, which is around checking of array initialization. By default, thus far, we have been unsound and not checked for initialization of array elements at all. Checking that array elements (for an array of @NonNull references) are initialized before they are used would be challenging and likely lead to many false positives. The Checker Framework Nullness Checker also handles array initialization unsoundly by default; see here. We should probably document this in our JSpecify mode documentation.

"public class Utilities {",
" public static String takesNullableVarargs(Object o, @Nullable Object... others) {",
" String s = o.toString() + \" \" + others.toString();",
" return s;",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I did add the test in bc46f2e. And I have now changed the logic so that we get an error, in 6b622c7. Great catch! My table didn't have this case as an error before, due to an attempt to make treatment of array types identical for regular arrays and varargs arrays. But there are already other cases where we don't have identical treatment. And the previous table made a @Nullable declaration annotation a no-op within the method body, which could be confusing. So this is now fixed, and I will update the table accordingly.

Comment on lines +412 to +413
" Object[] x = new Object[10];",
" takesVarargs(x, o);",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this is again due to unsound handling of array initializers, see previous comment

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.

Changes make sense to me!

@msridhar msridhar merged commit b7756ea into uber:master Sep 1, 2024
11 checks passed
@msridhar msridhar deleted the manu/issue-674 branch September 1, 2024 16:38
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.

Not-null varargs argument with Nullable elements - considered as Nullable
2 participants