Skip to content

#4076 Propagate JSpecify nullability annotations to the mapper implementation - #4112

Open
arjun2075 wants to merge 1 commit into
mapstruct:mainfrom
arjun2075:issue-4076-propagate-jspecify-nullability
Open

#4076 Propagate JSpecify nullability annotations to the mapper implementation#4112
arjun2075 wants to merge 1 commit into
mapstruct:mainfrom
arjun2075:issue-4076-propagate-jspecify-nullability

Conversation

@arjun2075

Copy link
Copy Markdown

Fixes #4076

Generated mapper implementations dropped the JSpecify @Nullable annotations declared on the mapper method they implement, so IDEs reported "Overriding method parameters are not annotated" on the generated @Override methods.

The nullability information was already resolved for null-check decisions (NullabilityResolver), but was never emitted into the generated signature. This propagates it:

  • Parameter and MappingMethod now carry the type of the @Nullable annotation to emit, and include it in their import types so the annotation is imported rather than fully qualified.
  • AbstractMappingMethodBuilder#propagateNullability resolves the nullability of the overridden method's return type and parameters via the existing MappingBuilderContext#getNullabilityInMapperScope, and is called from BeanMappingMethod.Builder and ContainerMappingMethodBuilder (covering bean, iterable, map and stream mapping methods).
  • The corresponding FTL templates emit the annotation inline, directly in front of the return type / parameter type.

Since JSpecify's @Nullable and @NonNull are TYPE_USE-only annotations, they are not valid in method-declaration position and therefore cannot go through the existing declaration-level annotations list.

Design note

Only an explicit @Nullable is reproduced. @NonNull is deliberately left out: inside a @NullMarked scope it is the implied default, so emitting it would add noise to every generated signature without changing the contract. Happy to change this if you'd prefer @NonNull to be explicit as well.

Nothing is propagated for forged methods, which do not override a mapper method. All behaviour stays gated behind the existing mapstruct.disableJSpecify option through the resolver.

Testing

  • New tests in org.mapstruct.ap.test.bugs._4076 covering @Nullable on parameters and return types, the generated import, and the container/update method paths (which route through ContainerMappingMethodBuilder rather than BeanMappingMethod.Builder).
  • Six existing JSpecify fixtures were updated: each declares @Nullable on the affected parameter, so the added annotation is the corrected output.
  • Full processor suite passes: 3653 tests, 0 failures, on both javac and ECJ. Checkstyle and the package-cycles enforcer also pass.

…er implementation

Generated mapper implementations dropped the JSpecify `@Nullable`
annotations declared on the mapper method they implement, so IDEs
reported "Overriding method parameters are not annotated" on the
generated `@Override` methods.

The nullability information was already resolved for null-check
decisions (NullabilityResolver), but was never emitted into the
generated signature. This propagates it:

* `Parameter` and `MappingMethod` now carry the type of the `@Nullable`
  annotation to emit, and include it in their import types so the
  annotation is imported rather than fully qualified.
* `AbstractMappingMethodBuilder#propagateNullability` resolves the
  nullability of the overridden method's return type and parameters via
  the existing `MappingBuilderContext#getNullabilityInMapperScope`, and
  is called from `BeanMappingMethod.Builder` and
  `ContainerMappingMethodBuilder` (covering bean, iterable, map and
  stream mapping methods).
* The corresponding FTL templates emit the annotation inline, directly
  in front of the return type / parameter type.

Since JSpecify's `@Nullable` and `@NonNull` are `TYPE_USE`-only
annotations, they are not valid in method-declaration position and
therefore cannot go through the existing declaration-level
`annotations` list.

Only an explicit `@Nullable` is reproduced. `@NonNull` is deliberately
left out: inside a `@NullMarked` scope it is the implied default, so
emitting it would add noise to every generated signature without
changing the contract. Nothing is propagated for forged methods, which
do not override a mapper method. All of this stays gated behind the
existing `mapstruct.disableJSpecify` option through the resolver.
@arjun2075
arjun2075 force-pushed the issue-4076-propagate-jspecify-nullability branch from cf162e3 to 9e41476 Compare August 14, 2026 15:15
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

Successfully merging this pull request may close these issues.

JSpecify: Propagate nullability annotations to mapper implementation

1 participant