JSpecify: support inference for calls to generic methods
This will be a significant challenge in general, but maybe we can handle the common cases and do something useful without having a full technique. See here for one test case:
https://github.com/uber/NullAway/blob/7a3d08895e2960adeb94f0d2d6d1205ae1c142d9/nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodTests.java#L141-L160
Hit this as well.
The specific case I ran into was in this form:
@NullMarked
public class Foo {
public static class Key<T extends @Nullable Object> {}
private static final Key<@Nullable Foo> KEY = new Key();
public static <T extends @Nullable Object> void setValue(Key<T> key, T value) {
}
public static void main() {
setValue(KEY, null);
}
}
Thanks for the example! We are working on this.
FYI, this (or something very similar) came up when Dropwizard upgraded to a new version of Caffeine. Fortunately, in their case, they appear to be better off just removing their usage of @Nullable.
Heh, this is coming up more with the new release because now our annotations within generics are being noticed more :P.
Here's a simpler example:
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class Foo {
public static void test(@Nullable Object foo) {
helper(foo);
}
private static <T extends @Nullable Object> void helper(T value) {
System.out.println(value);
}
}
Foo.java:10: warning: [NullAway] passing @Nullable parameter 'foo' where @NonNull is required
helper(foo);
^
(see http://t.uber.com/nullaway )
The best work-arounds I've found so far are:
- Use explicit generics at callsite:
public static void test(@Nullable Object foo) {
Foo.<@Nullable Object>helper(foo);
}
- Marking the helper as
@NullUnmarked:
@NullUnmarked
private static <T extends @Nullable Object> void helper(T value) {
System.out.println(value);
}
Just sharing in case it's helpful to anyone else.
I hope that #1131 will cover many common cases here, but it's a subtle change and we're still working on it. @agrieve once that PR is landed I may ask if you can test on Chromium with a snapshot build.
I'm glad to hear that explicit generics have worked for you. That what I would have hoped / expected, though we definitely aim to eliminate the need for these as much as possible.
Actually, small correction, we will handle the specific case from https://github.com/uber/NullAway/issues/1075#issuecomment-2698009946 in a follow-up to #1131
Not sure if that's the same one, but with:
public class Wrapper<T extends @Nullable String> {
private final T value;
public Wrapper(T value) {
this.value = value;
}
T unwrap() {
return value;
}
}
I am unable to write new Wrapper<@Nullable String>(null) and currently have to specify @NullUnmarked public Wrapper(T value)to make it work.
@sdeleuze I think your latest case may be #1155 rather than this one? I'll try to look at that one soon.
Indeed, looks like more related to #1155. Thanks!
is this the same case or is this a separate issue?
static <R> void invoke(Supplier<@Nullable R> supplier) {
}
invoke(() -> null);
error: [NullAway] Cannot pass parameter of type Supplier<Object>, as formal parameter has type Supplier<@org.jspecify.annotations.Nullable R>, which has mismatched type parameter nullability
invoke(() -> null);
^
@mhalbritter I'm afraid it is, you can try this as a workaround:
@NullUnmarked
static <R extends @Nullable Object> void invoke(@NonNull Supplier<R> supplier) {
}
Hi all, we've made significant improvements in generic method inference on the master branch, which should be released soon in version 0.12.10. I think we're at the point where we should open follow-up issues for specific limitations, to track things more carefully, and we can close this issue on initial support. I've gone through the reports here and opened up one follow-up issue, #1290. Also, #1157 needs to be re-opened. As far as I could see, the other examples reported here work now. We welcome follow-up issues for cases that still don't work! I'll comment back when the release is out.
NullAway 0.12.10 is now released with these changes.