-
Notifications
You must be signed in to change notification settings - Fork 299
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
Conversation
Codecov ReportAttention: Patch coverage is
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. |
nullaway/src/test/java/com/uber/nullaway/jspecify/JSpecifyArrayTests.java
Show resolved
Hide resolved
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.
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.
// This statement should be unreachable without assigning actual beforehand: | ||
Preconditions.checkNotNull(actual); |
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.
This is still the case, right? Is just that now NullAway can prove this statically?
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.
Correct, javac
's initialized variables check now proves it (along with the fact that we never assign null
to the variable)
nullaway/src/test/java/com/uber/nullaway/LegacyVarargsTests.java
Outdated
Show resolved
Hide resolved
nullaway/src/test/java/com/uber/nullaway/jspecify/JSpecifyVarargsTests.java
Show resolved
Hide resolved
"public class Utilities {", | ||
" public static String takesNullableVarargs(Object o, @Nullable Object... others) {", | ||
" String s = o.toString() + \" \" + others.toString();", | ||
" return s;", |
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.
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)?
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 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.
nullaway/src/test/java/com/uber/nullaway/jspecify/JSpecifyVarargsTests.java
Outdated
Show resolved
Hide resolved
nullaway/src/test/java/com/uber/nullaway/jspecify/JSpecifyVarargsTests.java
Outdated
Show resolved
Hide resolved
" Object[] x = new Object[10];", | ||
" takesVarargs(x, o);", |
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.
Again, why isn't this an error in JSpecify mode? Isn't the initialization here an array of 10 null
references?
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.
Yes, this is again due to unsound handling of array initializers, see previous comment
Co-authored-by: Lázaro Clapp <[email protected]>
Co-authored-by: Lázaro Clapp <[email protected]>
Co-authored-by: Lázaro Clapp <[email protected]>
Co-authored-by: Lázaro Clapp <[email protected]>
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 a lot for the detailed review @lazaroclapp!
// This statement should be unreachable without assigning actual beforehand: | ||
Preconditions.checkNotNull(actual); |
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.
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);", |
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.
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() + \" \";", |
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.
That reasoning only occurs in JSpecify mode. It is tested in JSpecifyVarargsTests#typeUseAndDeclarationOnBoth
and in other methods in that class.
" Object[] x = new Object[10];", | ||
" takesVarargs(x, o);", |
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.
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;", |
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 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.
" Object[] x = new Object[10];", | ||
" takesVarargs(x, o);", |
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.
Yes, this is again due to unsound handling of array initializers, see previous comment
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.
Changes make sense to me!
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):
@Nullable
varargs array@Nullable
array elements@Nullable
args at calls...
...
...
...
...
...
"
@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