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

@NonNull method type param for a @Nullable class type param confusion #1094

Open
ben-manes opened this issue Dec 15, 2024 · 3 comments
Open

Comments

@ben-manes
Copy link

ben-manes commented Dec 15, 2024

I think that I am getting contradictory warnings for this complex scenario. I am trying to annotate a test adapter where a Guava cache implements Caffeine's interfaces so that parameterized tests can assert against both implementations. This allows Guava's to be a golden reference for regression testing, compatibility, and intentional deviations.

The Caffeine cache was annotated where a nullable value is allowed for Map.compute style behaviors, even though null cannot be stored in the cache.

@NullMarked
public interface Cache<K, V extends @Nullable Object> {

  @Nullable
  V getIfPresent(K key);

  V get(K key, Function<? super K, ? extends V> mappingFunction);

  Map<K, @NonNull V> getAll(
      Iterable<? extends K> keys,
      Function<? super Set<? extends K>, ? extends Map<? extends K, ? extends @NonNull V>>
          mappingFunction);
}

As the adapter may return null, I tried adding the annotations

static class GuavaCache<K, V> implements Cache<K, @Nullable V>, Serializable {

  @Override
  public Map<K, @NonNull V> getAll(Iterable<? extends K> keys, Function<? super Set<? extends K>,
      ? extends Map<? extends K, ? extends V>> mappingFunction) { ... }

but this emits the warning

/Users/ben/projects/caffeine/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/GuavaCacheFromContext.java:203: warning: [NullAway] Method returns Map<K, @org.jspecify.annotations.NonNull V>, but overridden method returns Map<K, @org.jspecify.annotations.Nullable V>, which has mismatched type parameter nullability
    public Map<K, @NonNull V> getAll(Iterable<? extends K> keys, Function<? super Set<? extends K>,
                              ^
    (see http://t.uber.com/nullaway )
  Did you mean '@SuppressWarnings("NullAway") @Override'

If I specify that as @Nullable then it is happy and warns at the implementation's return logic. However, I don't believe this is correct given the interface makes it explicitly non-null. I can suppress these or use @NullUnmarked on the implementation. I'm unsure if this is a declaration or analysis mistake (cc @cpovirk).

@msridhar
Copy link
Collaborator

This may be the same underlying issue as #1091. When we are substituting type variables, we don't have the logic to preserve the annotations in the type we are substituting into. For this case, we are substituting @Nullable V for V inside @NonNull V and getting @Nullable V as the result, which is wrong I think. I'll keep this case in mind when working on #1091.

@ben-manes
Copy link
Author

Good news. I was able to fully enable the null analysis on the project (main, tests, benchmarks), with only a few of these undesirable suppressions needed. That exercise found a couple small mistakes in the test logic thanks to the stricter null checks required.

@msridhar
Copy link
Collaborator

Fantastic! And thanks for all the reports! We'll work on getting them fixed soon.

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

No branches or pull requests

2 participants