diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index 163c390b66..358b08f8ec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -681,7 +681,10 @@ private boolean targetDeclaringTypeIsNullMarked() { return false; } Element declaring = targetElement.getEnclosingElement(); - if ( !( declaring instanceof TypeElement ) ) { + while ( declaring != null && !( declaring instanceof TypeElement ) ) { + declaring = declaring.getEnclosingElement(); + } + if ( declaring == null ) { return false; } return ctx.getTypeFactory().getType( declaring.asType() ).isNullMarked(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/ErroneousIssue4079Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/ErroneousIssue4079Mapper.java new file mode 100644 index 0000000000..763fd99683 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/ErroneousIssue4079Mapper.java @@ -0,0 +1,15 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.bugs._4079; + +import org.jspecify.annotations.NullMarked; +import org.mapstruct.Mapper; + +@Mapper +@NullMarked +public interface ErroneousIssue4079Mapper { + Target map(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Issue4079Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Issue4079Test.java new file mode 100644 index 0000000000..52da98ada8 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Issue4079Test.java @@ -0,0 +1,37 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.bugs._4079; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.WithJSpecify; +import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult; +import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic; +import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +@IssueKey( "4079" ) +@WithJSpecify +public class Issue4079Test { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource(); + + @ProcessorTest + @WithClasses( { ErroneousIssue4079Mapper.class, Source.class, Target.class } ) + @ExpectedCompilationOutcome(value = CompilationResult.FAILED, + diagnostics = { + @Diagnostic(type = ErroneousIssue4079Mapper.class, + kind = javax.tools.Diagnostic.Kind.ERROR, + message = "Can't map potentially nullable source property \"nested\" to @NonNull " + + "constructor parameter \"nested\". Consider adding a defaultValue or " + + "defaultExpression.") + }) + public void nullableSourceToNonNullConstructorParamUnderNullMarkedShouldFail() { + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Source.java new file mode 100644 index 0000000000..f02d11f764 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Source.java @@ -0,0 +1,34 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.bugs._4079; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public class Source { + private final @Nullable Nested nested; + + public Source(@Nullable Nested nested) { + this.nested = nested; + } + + public @Nullable Nested getNested() { + return nested; + } + + public static class Nested { + private final String foo; + + public Nested(String foo) { + this.foo = foo; + } + + public String getFoo() { + return foo; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Target.java new file mode 100644 index 0000000000..612d25c896 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_4079/Target.java @@ -0,0 +1,33 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.bugs._4079; + +import org.jspecify.annotations.NullMarked; + +@NullMarked +public class Target { + private final Nested nested; + + public Target(Nested nested) { + this.nested = nested; + } + + public Nested getNested() { + return nested; + } + + public static class Nested { + private final String foo; + + public Nested(String foo) { + this.foo = foo; + } + + public String getFoo() { + return foo; + } + } +}