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

Guava v31.0.+ introducing @ParametricNullness breaks our builds #628

Closed
1 task done
holtherndon-stripe opened this issue Aug 2, 2022 · 8 comments
Closed
1 task done

Comments

@holtherndon-stripe
Copy link

Nullaway Version: 0.8.0
Guava Version: v31.0
Build System: Bazel version 4.2.2

  • If you think you found a bug, please include a code sample that reproduces the problem. A test case that reproduces the issue is preferred. A stack trace alone is ok but may not contain enough context for us to address the issue.

In the latest version of Google Guava, ParametricNullness is introduced. In v30.0 FutureCallback, for example, onSuccess is marked with @Nullable, however in v31.0 FutureCallback Guava changes this to @ParametricNullness. Since this annotation is unrecognized by Nullaway, it causes our builds to fail with the error:

pkg/NullawayParametricNullness.java:11: warning: [NullAway] passing @Nullable parameter 'result' where @NonNull is required
        futureCallback.onSuccess(result);
                                 ^
    (see http://t.uber.com/nullaway )
error: warnings found and -Werror specified

Source:

package pkg;

import com.google.common.util.concurrent.FutureCallback;
import javax.annotation.Nullable;

public class NullawayParametricNullness {
  public static <T> FutureCallback<T> wrapCallback(FutureCallback<T> futureCallback) {
    return new FutureCallback<T>() {
      @Override
      public void onSuccess(@Nullable T result) {
        futureCallback.onSuccess(result);
      }

      @Override
      public void onFailure(Throwable throwable) {
        futureCallback.onFailure(throwable);
      }
    };
  }
}
@msridhar
Copy link
Collaborator

msridhar commented Aug 2, 2022

Thanks for the report! My first thought as a quick fix is to just treat @ParametricNullness as if it were the same as @Nullable. That should certainly fix this issue. But I will try to dig and understand a bit better.

@lazaroclapp
Copy link
Collaborator

lazaroclapp commented Aug 2, 2022

@holtherndon-stripe : Potential work around and our own proposed plan forward in this comment upstream.

tl;dr: -XepOpt:NullAway:CustomNullableAnnotations=com.google.common.util.concurrent.ParametricNullness should get you unblocked and we plan to hard-code support in the next NullAway version. It's not a perfect map for the semantics of @ParametricNullness (and I am still confirming that we got those right), but it might be the best we can do until we have full support for nullability of generics (type parameters).

Edit: Apparently there are many different @ParametricNullness annotations in Guava. Might be easier to wait for a new release of NullAway which handles them all (currently have a PR up for this: #629 )

@rwinograd
Copy link

rwinograd commented Aug 2, 2022

Might be easier to wait for a new release of NullAway which handles them all (currently have a PR up for this: #629 )

OOC, roughly when would this release be?

lazaroclapp added a commit that referenced this issue Aug 2, 2022
)

Guava versions from 31.0 onwards do away with annotating methods as
returning or taking `@Nullable` if nullability depends on the type
parameter. Instead adopting JSpecify semantics for precise handling
of type parameters.

For compatibility (with Kotlin, among other tools), Guava still marks
such returns and arguments specially as `@ParametricNullness`.
    
While imprecise, we can handle Guava as annotated code to at least the
same level as we did previously, by treating `@ParametricNullness` as
an alias for `@Nullable`, until we have full type parameter / generics
support within NullAway.
    
To test this change without bumping our own Guava dependency, we
introduce a new test target: `:guava-recent-unit-tests`.
    
Additionally, since there are actually multiple instances of
`@ParametricNullness` in Guava (one per subpackage), we hard-code
a check for `com.google.common.*.ParametricNullness` in our core
`isNullableAnnotation(...)` check.

An alternative would be to extend `-XepOpt:NullAway:CustomNullableAnnotations`
to allow simple names or regular expressions, but that would make
the mechanism more costly in common case.

See also: #628, google/guava#6126
@lazaroclapp
Copy link
Collaborator

Might be easier to wait for a new release of NullAway which handles them all (currently have a PR up for this: #629 )

OOC, roughly when would this release be?

Aiming for the end of this week or early next week. However, if this is blocking people, I can also try and aim for tonight/tomorrow. There is some internal testing we'd like to do on an unrelated feature (unlikely to be enabled outside of Uber right now), but worst case it just means cutting two releases one soon after the other.

@rwinograd
Copy link

rwinograd commented Aug 3, 2022

I don't believe it's blocking. We wound up suppressing a large number of warnings for now, and those can be undone when we merge in the next version of NullAway.

It is somewhat time sensitive though, as I would like to avoid people too eagerly suppressing warnings and having to find them later.

@lazaroclapp
Copy link
Collaborator

lazaroclapp commented Aug 3, 2022

It is somewhat time sensitive though, as I would like to avoid people too eagerly suppressing warnings and having to find them later.

Given this, we are releasing NullAway 0.9.9 with the fix today, please let us know if you run into any issues!

Note two things:

  • In general, said fix involves a trade-off where false positives are possible for Guava APIs (as opposed to false negatives). We are explicitly treating Foo<T> as having @Nullable T if that type is permitted for any instance of Foo.
  • In theory, this is no different than the level of support we had for nullability in Guava before 31.0, but the fact that they have more precise tracking of nullability depending on type parameters, means Guava has added @ParametricNullness to places were @Nullable wasn't present before (see this comment) and might do so more liberally in the future.

Short term, this is the best option we have for handling 31.0 in a manner that is compatible with how we handled previous versions of Guava.

Long term, if both Guava and NullAway implement JSpecify semantics (or even some more rudimentary compatible generics / type parameter support to begin with), this will lead to more precise tracking of nullness information through Guava APIs (e.g. FutureCallback<@Nullable String> will allow null, and FutureCallback<String> will not, and NullAway should be able to track which is which). This is definitely in our roadmap, just not something we can implement in a short timeframe!

We are exploring some medium-term solutions too. On the user side, right now, a medium effort workaround exists for any new false positives: 0.9.9's treatment of @ParametricNullness can be augmented with custom library models to force specific classes within Guava to default to disallowing null, even if Guava itself says it's up to the type parameter.

(Edit: Already ran into one case where library models were needed in our own codebase for Guava classes using parametric nullness, see #632)

Will close this specific issue after a while or if we hear that NullAway 0.9.9 generally solves the issue for the time being :)

lazaroclapp added a commit that referenced this issue Aug 5, 2022
This is a follow up to #628 and our support for `@ParametricNullness` in NullAway 0.9.9.
It turns out that `com.google.common.io.Closer` annotates it's `register` method as:

```
  @CanIgnoreReturnValue
  @ParametricNullness
  public <C extends @nullable Closeable> C register(@ParametricNullness C closeable) {...}
```

Where the return is only really `@Nullable` if the argument is `@Nullable` (it is the same reference).

This PR adds a Library Model for this method, which is equivalent to adding the `@Contract("null -> null")`
annotation to it.

Note that, in order for this model to be helpful, `AcknowledgeLibraryModelsOfAnnotatedCode` 
must be set, which would seem to argue for making that the default.
@msridhar
Copy link
Collaborator

msridhar commented Sep 1, 2022

@holtherndon-stripe @rwinograd can this issue be closed now?

@holtherndon-stripe
Copy link
Author

Yep! This solved our issue, I'll go ahead and close it!

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

4 participants