Chromium uses NullAway to enforce JSpecify-style @Nullable
annotations. NullAway is a Error Prone plugin and runs as a static analysis step for targets without chromium_code = false
.
Chromium's NullAway configuration is as follows:
@Nullable
is TYPE_USE
.org.chromium.build.annotations
.//build/android:build_java
, which for convenience, is a default dep of all android_library
and java_library
targets.@NullMarked
.@Nullable
and @NonNull
are respected, but non-annotated types are permissive (return types are non-null and parameters are nullable).Preconditions
are modeled directly in NullAway.ChromeNullAwayLibraryModel
.onCreate()
methods are implicitly marked @Initializer
.assert foo != null
causes foo
to no longer be nullable.assumeNonNull(foo)
causes foo
to no longer be nullable without actually checking.We are actively opting classes into enforcement and hope to be complete by March 2025. See details in crbug.com/389129271.
// Plain Objects: private String mNonNullString; private @Nullable String mNullableString; private Outer.@Nullable Inner mNullableNestedType; // Arrays: private String @Nullable[] mNullableArrayOfNonNullString; private @Nullable String[] mNonNullArrayOfNullableString; // Generics: private List<@Nullable String> mNonNullListOfNullableString; private @Nullable Callback<@Nullable String> mNullableCallbackOfNullableString; // Does not compile (annotation must come immediately before type): @Nullable private String mInvalidAnnotation;
NullAway analyzes code on a per-method basis. These annotations tell it how about pre/post conditions:
// Using this with non-private methods never makes sense. @RequiresNonNull("mNullableString") private void usesNullableString() { // No warning: if (mNullableString.isEmpty()) { ... } } @EnsuresNonNull("mNullableString") private void codeCanCallThisAndThenUseNullableString() { // This will warn if mNullableString is @Nullable at any egress. assert mNullableString != null; } // If this method returns true, then mThing is non-null. @EnsuresNonNullIf("mThing") private boolean isThingEnabled() { return mThing != null; } // Also works with static fields and negated return values. @EnsuresNonNullIf(value={"sThing1", "sThing2"}, result=false) private static boolean isThingEnabled() { return sThing1 == null || sThing2 == null; } // If foo is null, this method returns false. // Most other forms of contracts are not supported. @Contract("null -> false") private boolean isParamNull(@Nullable String foo) { return foo != null; }
Construction:
onCreate()
or initialize()
), you can tell NullAway to not check for null until after methods annotated with @Initializer
are called.@Initializer
can also be used for static
methods, which impacts warnings for static
fields.Destruction:
For classes with destroy()
methods that set fields to null
that would otherwise be non-null, you can either:
@Nullable
and add !isDestroyed()
asserts / guards where necessary (where isDestroyed()
is annotated with @EnsuresNonNullIf(value=..., result=false)
), ordestroy()
method with @SuppressWarnings("NullAway")
.@CalledByNative
methods (crbug/389192501).assert
statements for Java->Native methods (when @NullMarked
exists).It does not understand when local variables contain non-null information.
It does not infer nullness of inferred generics.
Validation of @Contract
within methods that use it is broken.
Q: Why not use Checker Framework?
A: Chromium already uses Error Prone, so NullAway was easy to integrate.
Q: How do @NullUnmarked
and @SuppressWarnings("ErrorProne")
differ?
A: NullAway treats these two the same. In Chromium, @SuppressWarnings
is used when a warning is unavoidable (appeasing NullAway would make the code worse), and @NullUnmarked
is used when a method has not yet been updated to support @Nullable
annotations (its a remnant of our automated adding of annotations).
Q: Can I use JSpecify Annotations?
A: Yes. For code that will be mirrored and built in other environments, it is best to use JSpecify annotations. You'll probably want to set:
deps += [ "//third_party/android_deps:org_jspecify_jspecify_java" ] # Prevent automatic dep on build_java. chromium_code = false # Do not let chromium_code = false disable Error Prone. enable_errorprone = true