Skip to content

JSpecify Support

Manu Sridharan edited this page Oct 28, 2025 · 18 revisions

JSpecify is an effort to standardize annotations for Java static analysis, and JSpecify 1.0 includes nullability annotations. Out of the box, NullAway supports JSpecify annotations in its standard mode of checking. If you are a current NullAway user, you should just be able to swap in JSpecify annotations for whatever nullability annotations you were using before, and you should not get any new NullAway errors. If you see problems, please report an issue.

Type-Use Annotation Placement

A caveat to the above is that starting with NullAway 0.12.0, NullAway by default requires that JSpecify annotations be written in the correct place on qualified and array types. So, e.g., if we have a field String[] x, to mark x itself as @Nullable, you must write String @Nullable [] x if using JSpecify's @Nullable annotation or another type-use @Nullable annotation. And, if you have a varargs argument foo(Object... args) and you would like the args array itself to be @Nullable, you must write foo(Object @Nullable... args). See here for further details on type-use annotation syntax, and here and here for the gory details on how NullAway interprets different types of annotations. This change may lead to NullAway reporting new errors in existing code. We provide a compatibility flag -XepOpt:NullAway:LegacyAnnotationLocations to use NullAway's old logic for interpreting annotations to ease the transition, but we expect to remove this flag in a future release.

JSpecify Mode

We are also working on adding support for full JSpecify semantics for these annotations, including annotations on generic types. You can enable our work-in-progress checking by passing the configuration flag -XepOpt:NullAway:JSpecifyMode=true when running NullAway. This mode is still under development and may report false positive warnings on your code. Please report these cases and we will address them. Major outstanding features still to be supported include:

  • pulling in JDK annotations from https://github.com/jspecify/jdk. This will enable much better checking of uses of types like Collections.
  • full inference support for generic methods
  • wildcard types
  • checking of generic class implementations (i.e., checking that generic type variables are used correctly). Thus far we have focused on supporting uses of generic classes, but not on checking their internals.

Not supporting the above features may lead to false negatives (missed issues). We are slowly working towards supporting them, and any help from the community is appreciated, from issue reports to pull requests.

Supported JDK versions

When building with JSpecify mode enabled, NullAway (as of version 0.12.11) checks that it is running on a version of javac that supports reading type use annotations from bytecodes (see https://github.com/uber/NullAway/pull/1245), which is important for predictable and complete checking behavior. Specifically, NullAway checks that either (1) the running javac is from JDK 22 or higher, or (2) the -XDaddTypeAnnotationsToSymbol=true flag has been passed to javac. The -XDaddTypeAnnotationsToSymbol=true flag enables support for type use annotations in bytecodes for JDK 21 as of release 21.0.8, and may eventually be ported back to JDK 17. Note that he -XDaddTypeAnnotationsToSymbol=true flag is not supported by Oracle JDK 21; you must use an OpenJDK build like those from Temurin or Zulu. We recommend using the latest javac version possible when building your code to get all the latest bug fixes; the --release flag enables targeting older JDK versions while still using the latest compiler.

JSpecify and Guava

As of version 33.4.1, the Guava library uses JSpecify @NullMarked annotations for most of its packages and classes. This means that NullAway will automatically perform stricter checking of calls into Guava code, whether or not NullAway is run in JSpecify mode. Outside of JSpecify mode, NullAway may report new false positive errors, as it will no longer allow @Nullable expressions to be passed to Guava methods whose parameter type is a type variables. (See discussion, e.g., in https://github.com/uber/NullAway/issues/1199, https://github.com/uber/NullAway/issues/1186, or https://github.com/PicnicSupermarket/error-prone-support/pull/1598.) Running NullAway in JSpecify mode should remove these false positives, but it may require annotating type arguments as @Nullable, including in cases where type arguments were previously inferred (due to current limitations in NullAway's inference, see https://github.com/uber/NullAway/issues/1075). To maximize null safety, we recommend running NullAway in JSpecify mode with additional annotations, but you may still need to suppress some false positives. Alternately, if you do not want to switch to JSpecify mode yet, you will have to suppress some false positives when updating to a recent Guava version.

Clone this wiki locally