-
Notifications
You must be signed in to change notification settings - Fork 27
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
Adoption of JSpecify annotations in Guava #239
Comments
(We think we can do this without needing a GWT module: We already strip |
I should note that Guava's transitional I know I've done at least one other experiment that relied on |
A user inside Google just noticed that IntelliJ's support for JSpecify annotations turned on by default recently. The user reports some resulting false positives in some generics-heavy code. That's expected, and the question is just how widespread such false positives will be. We'll keep an eye out for more reports, and we can use those reports as input for the decision about which annotations to use in Guava when. |
Hmm. To recap:
So: I wonder if some of the new IntelliJ Java checking for JSpecify annotations was already happening for other annotations for our external users. If so, then there would be no impact for IntelliJ Java users if we switched to JSpecify externally. It's probably not quite that simple, but it might be that the change would not be as significant as I'd initially thought. |
…tations when they're available. They're never available under an Android VM, but we also test guava-android under a JRE (which is always Java 8+ now, which makes our implementation job a bit easier), so the annotations _are_ available there. This is a step toward letting us [use type-use annotations more widely in Guava](jspecify/jspecify#239). In particular, it unblocks cl/521011962 (which in turn reduces the number of merge conflicts we'll see when using type-use annotations in our tests), and it helps our other testing of type-use annotations in Guava. In order to implement this, I copied two methods that were previously only available in guava-jre to make them also available in guava-android for `NullPointerTester` there: `Invokable.getAnnotatedReturnType()` and `Parameter.getAnnotatedType()`. Those methods do not work under and Android VM (where, again, type-use annotations aren't available), but they're useful for running our tests under a JRE. FYI: We did see one project that needed some new `-dontwarn` lines in [our Proguard config](#2117) as a result of this CL. But we expect this to be unlikely, especially since reflection is typically a bad idea under Android. (An alternative approach would be for us to expose two _new_ methods that are similar to `getAnnotatedReturnType()` and `getAnnotatedType()` but that declare their return type as plain `Object`. That would be safer, but I think there's actually more value in testing the more aggressive approach: We'd like to someday be more aggressive about adding APIs that use Java 8+ types in guava-android, so it's nice to test them on low-stakes `@Beta` APIs that shouldn't be used from Android to begin with.) On the plus side, we no longer need to perform reflection on account of j2objc: As planned in cl/367051816, j2objc was subsequently updated in cl/367072772 to contain stub implementations of the necessary `TypeVariable` methods. RELNOTES=n/a PiperOrigin-RevId: 521795608
…tations when they're available. They're never available under an Android VM, but we also test guava-android under a JRE (which is always Java 8+ now, which makes our implementation job a bit easier), so the annotations _are_ available there. This is a step toward letting us [use type-use annotations more widely in Guava](jspecify/jspecify#239). In particular, it unblocks cl/521011962 (which in turn reduces the number of merge conflicts we'll see when using type-use annotations in our tests), and it helps our other testing of type-use annotations in Guava. In order to implement this, I copied two methods that were previously only available in guava-jre to make them also available in guava-android for `NullPointerTester` there: `Invokable.getAnnotatedReturnType()` and `Parameter.getAnnotatedType()`. Those methods do not work under and Android VM (where, again, type-use annotations aren't available), but they're useful for running our tests under a JRE. FYI: We did see one project that needed some new `-dontwarn` lines in [our Proguard config](#2117) as a result of this CL. But we expect this to be unlikely, especially since reflection is typically a bad idea under Android. (An alternative approach would be for us to expose two _new_ methods that are similar to `getAnnotatedReturnType()` and `getAnnotatedType()` but that declare their return type as plain `Object`. That would be safer, but I think there's actually more value in testing the more aggressive approach: We'd like to someday be more aggressive about adding APIs that use Java 8+ types in guava-android, so it's nice to test them on low-stakes `@Beta` APIs that shouldn't be used from Android to begin with.) On the plus side, we no longer need to perform reflection on account of j2objc: As planned in cl/367051816, j2objc was subsequently updated in cl/367072772 to contain stub implementations of the necessary `TypeVariable` methods. RELNOTES=n/a PiperOrigin-RevId: 521795608
…tations when they're available. They're never available under an Android VM, but we also test guava-android under a JRE (which is always Java 8+ now, which makes our implementation job a bit easier), so the annotations _are_ available there. This is a step toward letting us [use type-use annotations more widely in Guava](jspecify/jspecify#239). In particular, it unblocks cl/521011962 (which in turn reduces the number of merge conflicts we'll see when using type-use annotations in our tests), and it helps our other testing of type-use annotations in Guava. In order to implement this, I copied two methods that were previously only available in guava-jre to make them also available in guava-android for `NullPointerTester` there: `Invokable.getAnnotatedReturnType()` and `Parameter.getAnnotatedType()`. Those methods do not work under and Android VM (where, again, type-use annotations aren't available), but they're useful for running our tests under a JRE. FYI: We did see one project that needed some new `-dontwarn` lines in [our Proguard config](#2117) as a result of this CL. But we expect this to be unlikely, especially since reflection is typically a bad idea under Android. (An alternative approach would be for us to expose two _new_ methods that are similar to `getAnnotatedReturnType()` and `getAnnotatedType()` but that declare their return type as plain `Object`. That would be safer, but I think there's actually more value in testing the more aggressive approach: We'd like to someday be more aggressive about adding APIs that use Java 8+ types in guava-android, so it's nice to test them on low-stakes `@Beta` APIs that shouldn't be used from Android to begin with.) On the plus side, we no longer need to perform reflection on account of j2objc: As planned in cl/367051816, j2objc was subsequently updated in cl/367072772 to contain stub implementations of the necessary `TypeVariable` methods. RELNOTES=n/a PiperOrigin-RevId: 521795608
…tations when they're available. They're never available under an Android VM, but we also test guava-android under a JRE (which is always Java 8+ now, which makes our implementation job a bit easier), so the annotations _are_ available there. This is a step toward letting us [use type-use annotations more widely in Guava](jspecify/jspecify#239). In particular, it unblocks cl/521011962 (which in turn reduces the number of merge conflicts we'll see when using type-use annotations in our tests), and it helps our other testing of type-use annotations in Guava. In order to implement this, I copied two methods that were previously only available in guava-jre to make them also available in guava-android for `NullPointerTester` there: `Invokable.getAnnotatedReturnType()` and `Parameter.getAnnotatedType()`. Those methods do not work under and Android VM (where, again, type-use annotations aren't available), but they're useful for running our tests under a JRE. FYI: We did see one project that needed some new `-dontwarn` lines in [our Proguard config](#2117) as a result of this CL. But we expect this to be unlikely, especially since reflection is typically a bad idea under Android. (An alternative approach would be for us to expose two _new_ methods that are similar to `getAnnotatedReturnType()` and `getAnnotatedType()` but that declare their return type as plain `Object`. That would be safer, but I think there's actually more value in testing the more aggressive approach: We'd like to someday be more aggressive about adding APIs that use Java 8+ types in guava-android, so it's nice to test them on low-stakes `@Beta` APIs that shouldn't be used from Android to begin with.) On the plus side, we no longer need to perform reflection on account of j2objc: As planned in cl/367051816, j2objc was subsequently updated in cl/367072772 to contain stub implementations of the necessary `TypeVariable` methods. RELNOTES=n/a PiperOrigin-RevId: 522214387
In #24 (comment), I mention another reason to stick with declaration annotations for now: Type-use annotations on arrays don't trigger kotlinc errors, only warnings. (That is the case even for people who set the flags to otherwise promote nullness warnings to errors.) That bug may well be fixed [edit: happening in Kotlin 2.0.20] before we'd even be considering migrating fully from declaration to type-use annotations, anyway. |
Hmm, I should probably also test what happens if users use a JSpecify-annotated Guava wihout enabling If there were a problem, it would at least not lead to outright errors (neither at runtime nor at compile time), since JSpecify annotations produce only warnings by default. (And if a user is willing to opt in to making JSpecify-related warnings into errors, then it could be inconvenient but ultimately reasonable to have to ask that user to also set |
Good: The following works fine with val f: Function<Any?, String> = constant("") Interestingly, if I change |
Note also that Guava recently started using |
ascopes reports problems when building Javadoc against JSpecify, at least under certain settings: maxcellent/javadoc.io#182 I'd need to read more to understand whether there is anything we can fix on the JSpecify side. At first glance, I get the impression that Guava (and other motivated consumers) could work around it with |
It's looking like IntelliJ always treats JSpecify annotations as producing warnings only, even if we add [edit: Note also that we've seen a rare few cases in which kotlinc produces errors with |
https://stackoverflow.com/q/77915170/28465 suggests that Guava's current nullness annotations (perhaps specifically its usages of My suspicion, though, is that most nullness annotations are not recognized by Eclipse by default. The thing I'd have to look into is whether Eclipse nowadays lets you specify multiple annotations to treat like If Eclipse still supports only one annotation, then the main takeaway is that switching to JSpecify may improve things for Eclipse users: At least then, we'll consistently use the same annotation for the same thing (instead of mixing |
Oops, I should have waited until I clicked that final link on the Eclipse site... :)
So multiple annotations are supported nowadays. |
My impression from the recent activity on KT-59138 (and some quick testing) is that Kotlin 2.0 [edit: now deferred until at least 2.1.0] is going to start treating types like For example, today, code like this produces an error: fun go(f: ListenableFuture<String?>): String = f.get() Ditto for this slightly more complex code: fun go(m: Multimap<String, String?>, s: String): String = m.get(s).iterator().next() But with K2, neither of those will be an error anymore. Granted, K2 still produces an error for this similar code, perhaps from magic in its handling of fun go(m: Multiset<String?>): String = m.iterator().next() So I don't know how widespread the impact of the change would be for Guava types. I'm also not sure how much we can do about it until we're ready to use exclusively JSpecify annotations: Even though it's possible that we're at the point that we could use |
This is a minor thing (and not specific to Guava), but: I get the impression that type-use annotations show up in the Summary section of rendered Javadoc but that declaration annotations do not. See, e.g., The theory would be that seeing the nullness annotations in the summary is superior to having to look for them in the details, but in fairness, they can be noisy (worsened by google/guava#6790). Admittedly the mix of sometimes showing them and sometimes not showing them may be the worst of both worlds. |
A few things (about Kotlin unless otherwise noted) that I researched/tested today (most of which I was aware of but some of which might have been new):
I'll try to synthesize that into a proposed plan, maybe as soon as later today. |
Here's my 4-phase proposal for comment by @netdpb and others. I've written it mostly from the perspective of users of Kotlin, since Kotlin has the most complete JSpecify/nullness support of tools in common use, but I'm taking into account what I know about other tools. We could implement Phase 1 and Phase 2 today, or we could wait until JSpecify 1.0.0 or some other time of our choosing. Phase 1: Replace
|
google/truth#1320 points out that a usage of |
As discussed in google/truth#1320 (comment), we may want to put Guava's We'd then want to opt out (with I'm on the fence about this. |
[note to self: See also Google-internal issue 192446998.]
(We'd spoken a while back about filing issues in the JSpecify tracker for some tasks that other projects would take but that have interdependencies worth tracking here. Here's one such issue.)
Currently, Guava is annotated with a combination of jsr305 annotations (both jsr305's built-in nullness annotations and Guava's custom "nicknames" built on jsr305) and Checker Framework annotations. For the most part, tools don't care too much which nullness annotations a project uses, but there's at least one big exception, which is Kotlin.
The annotations we've chosen have the advantage of mostly producing errors in Kotlin by default: Both the built-in jsr305 nullness annotations and the CF annotations do. The caveats are:
-Xtype-enhancement-improvements-strict-mode
.@TypeQualifierDefault
annotations, all of which are named "@ElementTypesAreNonnullByDefault
"] currently produce only warnings until users set-Xjsr305=strict
.With JSpecify annotations currently producing only warnings for Kotlin users, the ideal first step (as soon as the JSpecify annotations are "stable enough" that we're comfortable) would be to add
@NullMarked
to our annotated classes while leaving the existing annotations in place. This would let users who opt in to-Xjspecify-annotations=strict
(and additionally opt in to-Xtype-enhancement-improvements-strict-mode
[edit: or maybe that's not necessary]) see Guava as almost completely annotated. [edit: Or would it be "overridden" by@ElementTypesAreNonnullByDefault
or vice versa? But again, both flags are currently warning-only.] Aside from that, it may be a no-op for Kotlin users (I'd have to think more to confirm that), though it may help users of other tools by then.Later, once JSpecify annotations produce errors by default in a Kotlin release (and that release is out for long enough that we're comfortable relying on it), we could remove the other annotations, replacing them with JSpecify annotations where necessary. In particular, we could remove one kind of jsr305 nickname that forces Kotlin users to still see platform types, even if they've set all the applicable flags.
The text was updated successfully, but these errors were encountered: