Priority and scope
P2: a valid patch mapping silently calls a different nested-updater overload from the one selected for the configured source type.
Confirmed during the release audit on candidate develop commit 9c48bfd59fe586097ca18c4289c85f03e93e30bc and published 3.0.0 commit b9697f2f91aa737360479a434f7d3cc01ccaa8fd. Both return the wrong sentinel value. This is a pre-existing defect, not a regression introduced by the recent fixes.
Minimal reproduction
#nullable enable
using Mammoth.LiteMapper;
[LiteMapper(IgnoreNullSourceMembers = true)]
public static partial class Mapper
{
[MapProperty(Source = "Container.Child",
Target = nameof(Target.Child), Use = nameof(ApplyChild))]
public static partial Target Apply(Source source, Target target);
private static void ApplyChild(ChildSource? source, ChildTarget target)
=> target.Value = 7;
private static void ApplyChild(ChildSource source, ChildTarget target)
=> target.Value = 99;
}
public sealed class Source { public Holder Container { get; } = new(); }
public sealed class Holder
{
public ChildSource? Child { get; set; } = new ChildSource();
}
public struct ChildSource { }
public sealed class Target { public ChildTarget Child { get; } = new(); }
public sealed class ChildTarget { public int Value { get; set; } }
// var target = new Target();
// Mapper.Apply(new Source(), target);
// target.Child.Value is 99; expected 7.
The same failure occurs for an explicitly configured direct path Source = "Child" after moving Child onto Source. The automatic direct-member [DefaultMapping] control returns 7 on the candidate.
Expected and actual
- Expected:
ChildSource? selects and invokes ApplyChild(ChildSource?, ChildTarget), returning sentinel 7.
- Actual: generation selects that updater but a patch pattern capture unwraps the nullable value to
ChildSource. The emitted ordinary C# call then binds to ApplyChild(ChildSource, ChildTarget), returning sentinel 99.
- Null source values must still skip the update and retain the existing child and its value.
The configured-path branch in updater argument emission passes the unwrapped capture without restoring the selected nullable parameter type.
Contract and acceptance criteria
Specification sections 9.4, 11.1, 11.4, 14.4, 16.1 and 16.3 require explicit selection, identity-compatible overload preference, single evaluation and patch null skipping.
- Add failing runtime and generated-compilation tests for configured direct and dotted nullable-value source paths with competing nullable/non-nullable updater overloads.
- Invoke the exact selected overload for both void and destination-returning nested updaters; retain target identity/assignment semantics.
- Preserve null skipping for missing leaf/intermediate values, exactly-once getter evaluation, and earlier assignments when a later member throws.
- Preserve automatic direct-member, non-nullable source, nullable-reference source, and non-patch/Throw controls.
- Verify warning-free, deterministic C# 9-compatible output, a normally compiled/package-consumer fixture, and relevant full Windows/Linux and Roslyn CI.
- Keep public API and specification semantics unchanged.
Duplicate review and boundaries
Reviewed existing repository issues, including closed #13/#16 and #29/#43. Those cover direct-member capture reuse/Throw behavior; this issue covers configured-path nullable-value overload binding. Do not reopen or broaden those completed fixes.
Handle this defect independently of the separate nullable enum-path finding. No release, tag or package publication is part of this issue.
Prepared with OpenAI Codex.
Priority and scope
P2: a valid patch mapping silently calls a different nested-updater overload from the one selected for the configured source type.
Confirmed during the release audit on candidate
developcommit9c48bfd59fe586097ca18c4289c85f03e93e30bcand published 3.0.0 commitb9697f2f91aa737360479a434f7d3cc01ccaa8fd. Both return the wrong sentinel value. This is a pre-existing defect, not a regression introduced by the recent fixes.Minimal reproduction
The same failure occurs for an explicitly configured direct path
Source = "Child"after movingChildontoSource. The automatic direct-member[DefaultMapping]control returns 7 on the candidate.Expected and actual
ChildSource?selects and invokesApplyChild(ChildSource?, ChildTarget), returning sentinel 7.ChildSource. The emitted ordinary C# call then binds toApplyChild(ChildSource, ChildTarget), returning sentinel 99.The configured-path branch in updater argument emission passes the unwrapped capture without restoring the selected nullable parameter type.
Contract and acceptance criteria
Specification sections 9.4, 11.1, 11.4, 14.4, 16.1 and 16.3 require explicit selection, identity-compatible overload preference, single evaluation and patch null skipping.
Duplicate review and boundaries
Reviewed existing repository issues, including closed #13/#16 and #29/#43. Those cover direct-member capture reuse/Throw behavior; this issue covers configured-path nullable-value overload binding. Do not reopen or broaden those completed fixes.
Handle this defect independently of the separate nullable enum-path finding. No release, tag or package publication is part of this issue.
Prepared with OpenAI Codex.