Make a codebase null-safe
io.moderne.nullability.NullSafetyio.moderne.recipe:rewrite-nullabilityMake Java code null-safe end to end. Infers and adds JSpecify @Nullable/@MonotonicNonNull from the code's own signals (returns, parameters, fields, override hierarchies, Kotlin call sites, and the JSpecify generic frontier), then detects and repairs the residual nullability violations — dereferences, unboxing, switch, enhanced-for, passing a nullable argument, nullable returns, uninitialized non-null fields, and override consistency — directly from the LST. Behavior-preserving and idempotent; run to a fixpoint over recipe cycles. If the code still uses non-JSpecify annotation flavors, run org.openrewrite.java.jspecify.MigrateToJSpecify first.
Usage
This recipe has no required configuration options. You’ll need the Moderne CLI configured before running the command below.
mod run . --recipe io.moderne.nullability.NullSafetyIf the recipe isn’t available locally, install it with:
mod config recipes jar install io.moderne.recipe:rewrite-nullability:0.3.1Options
| Name | Type | Description |
|---|---|---|
annotatedPackages | String | Comma-separated package prefixes to limit the fix to (matching NullAway's AnnotatedPackages option). When unset, the recipe fixes all packages.e.g. com.example |
assertionStyle | AssertionStyle | The non-null assertion form the residual Wrap* fixers emit at a last-resort site. AUTO (the default) inspects the classpath: Guava present (com.google.common) emits Preconditions.checkNotNull, otherwise Objects.requireNonNull. CAST_TO_NON_NULL is explicit-only (it is suppression-like — no runtime throw) and never auto-selected. |
Definition
This recipe runs the following recipes in order.
- Migrate
@NonNullApi/ParametersAreNonnullByDefaultto JSpecify@NullMarkedio.moderne.nullability.MigrateNonNullApiToNullMarked - Add
@NullMarkedto every packageio.moderne.nullability.AddNullMarkedToAllPackages - Compose JSpecify best practices (intra-body nullability inference)
io.moderne.nullability.ComposeJSpecifyBestPractices - Add
@Nullableto methods that can return nullio.moderne.nullability.AddNullableToReturnType - Add
@Nullableto method parameters that can receive nullio.moderne.nullability.AddNullableToParameterCrossFile - Add
@Nullableto fields that can hold nullio.moderne.nullability.AddNullableToField - Propagate
@Nullableacross override relationshipsio.moderne.nullability.PropagateNullableAcrossOverrides - Add
@Nullableto Java returns from Kotlin call sitesio.moderne.nullability.AddNullableFromKotlinCallSites - Add
@Nullableto array element types that can hold nullio.moderne.nullability.AddNullableToArrayElementType - Add
@Nullableto collection and map type arguments that hold null elementsio.moderne.nullability.AddNullableToTypeArgument - Add a
@Nullableupper bound to a pass-through type parameter fed a null-returning lambdaio.moderne.nullability.AddNullableBoundToPassthroughTypeParameter - Add
@Nullableto a functional-interface return type argument fed a null-returning lambdaio.moderne.nullability.AddNullableToFunctionalReturnArgument - Remove a redundant declaration-position
@Nullableon a method returnio.moderne.nullability.RemoveRedundantNullableOnMethodReturn - Remove a redundant
@NonNullannotation under@NullMarkedio.moderne.nullability.RemoveRedundantNonNullAnnotation - Relocate a misplaced leading
@Nullableon a primitive array to the array referenceio.moderne.nullability.RelocateNullableToArrayReference - Move a leading
@Nullableto the type-use positionio.moderne.nullability.MoveNullableToTypeUsePosition - Replace
@Nullablewith@MonotonicNonNullon a lazily-initialized fieldio.moderne.nullability.ReplaceNullableWithMonotonicNonNullOnLazyField - Hoist a guarded
@Nullablefield read into a local variableio.moderne.nullability.HoistNullableFieldReadIntoLocal - Remove a provably-dead
if (x == null)guardio.moderne.nullability.RemoveProvablyDeadNullGuard - Add a
@Contractnullness contract to a validation helperio.moderne.nullability.AddNullnessContractToValidationHelper - Route a guarded raw accessor through its present
Optionalio.moderne.nullability.CollapseOptionalPresentGuardToGet - Extract a repeated
@Nullableinvocation into a local variableio.moderne.nullability.ExtractRepeatedNullableInvocationToLocal - Replace nullable
x.toString()withString.valueOf(x)io.moderne.nullability.ReplaceNullableToStringWithStringValueOf - Make a nullable
Booleancondition null-safe withBoolean.TRUE.equals(...)io.moderne.nullability.SafeNullableBooleanCondition - Return an empty collection instead of
nullio.moderne.nullability.ReturnEmptyCollectionInsteadOfNull - Relax
Optional.oftoOptional.ofNullableon nullable valuesio.moderne.nullability.RelaxOptionalOfToOfNullable - Add
@MonotonicNonNullto an uninitialized fieldio.moderne.nullability.AddMonotonicNonNullToUninitializedField - Align override nullability with the supertype
io.moderne.nullability.AlignOverrideNullabilityWithSupertype - Wrap nullable arguments passed to non-null parameters in
requireNonNullio.moderne.nullability.WrapNullableArgumentInRequireNonNull - Wrap nullable dereferenced values in
requireNonNullio.moderne.nullability.WrapNullableDereferenceInRequireNonNull - Wrap nullable values that are auto-unboxed in
requireNonNullio.moderne.nullability.WrapNullableUnboxingInRequireNonNull - Wrap nullable
switchselectors inrequireNonNullio.moderne.nullability.WrapNullableSwitchSelectorInRequireNonNull - Wrap a nullable for-each iterable in
requireNonNullio.moderne.nullability.WrapNullableForEachIterableInRequireNonNull - Wrap nullable thrown expressions in
requireNonNullio.moderne.nullability.WrapNullableThrownExpressionInRequireNonNull - Add
@Nullableto a method that can return nullio.moderne.nullability.AddNullableToNullReturningMethod - Add
@Nullableto a field assigned a nullable valueio.moderne.nullability.AddNullableToNullAssignedField - Add
@Nullableto a mismatched generic type argumentio.moderne.nullability.AddNullableToMismatchedTypeArgument - Move a leading
@Nullableto the type-use positionio.moderne.nullability.MoveNullableToTypeUsePosition
Data tables
Structured output this recipe can produce.
- Null-safety fixes by rungEach residual null-safety fix applied, tagged with the precedence rung that produced it, so the share bottoming out at a last-resort `requireNonNull` assertion can be tracked.
io.moderne.nullability.table.NullFixes - Declined null-safety fixes needing reviewEach site the recipe declined to auto-fix because the fix is a design decision (the value is explicitly `null` on some path, so the slot likely should be `@Nullable`), for human triage.
io.moderne.nullability.table.DeclinedNullFixes - Source files that had resultsSource files that were modified by the recipe run.
org.openrewrite.table.SourcesFileResults - Source files that had search resultsSearch results that were found during the recipe run.
org.openrewrite.table.SearchResults - Source files that errored on a recipeThe details of all errors produced by a recipe run.
org.openrewrite.table.SourcesFileErrors - Recipe performanceStatistics used in analyzing the performance of recipes.
org.openrewrite.table.RecipeRunStats