mapperly icon indicating copy to clipboard operation
mapperly copied to clipboard

Incorrect nullable handling with implicit operators

Open MatthewSmit-Scope opened this issue 1 year ago • 2 comments

Describe the bug When mapping from a nullable type (either struct or class) to a type that has an implicit operator, mapperly incorrectly generates null handling checks. This occurs both when mapping directly, as in the example below, or when mapping a class with properties of this type.

As the implicit operator can take a nullable type, I would expect the nullable value to be passed in without the null check. In cases where the implicit operator can't take a nullable type (e.g., int? to TrackedProperty in the example below), the current behaviour of generating the null check is correct.

Declaration code

using Riok.Mapperly.Abstractions;

[Mapper]
public partial class Simple1
{
    public static partial TrackedProperty<int?> ToFoo(int? source);
}

[Mapper]
public partial class Simple2
{
    public static partial Dst ToFoo(Src source);
}

public class Src
{
    public int? Foo { get; set; }
}

public class Dst
{
    public TrackedProperty<int?> Foo { get; set; }
}

public readonly record struct TrackedProperty<T>
{
    private readonly T value;

    public TrackedProperty(T value)
    {
        this.value = value;
    }

    public static implicit operator TrackedProperty<T>(T value)
    {
        return new TrackedProperty<T>(value);
    }
}

Actual relevant generated code

// <auto-generated />
#nullable enable
public partial class Simple1
{
    [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.0.0.0")]
    public static partial global::TrackedProperty<int?> ToFoo(int? source)
    {
        return source == null ? throw new System.ArgumentNullException(nameof(source)) : (global::TrackedProperty<int?>)source.Value;
    }
}

public partial class Simple2
{
    [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.0.0.0")]
    public static partial global::Dst ToFoo(global::Src source)
    {
        var target = new global::Dst();
        if (source.Foo != null)
        {
            target.Foo = (global::TrackedProperty<int?>)source.Foo.Value;
        }
        return target;
    }
}

Expected relevant generated code

// <auto-generated />
#nullable enable
public partial class Simple1
{
    [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.0.0.0")]
    public static partial global::TrackedProperty<int?> ToFoo(int? source)
    {
        return (global::TrackedProperty<int?>)source;
    }
}

public partial class Simple2
{
    [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.0.0.0")]
    public static partial global::Dst ToFoo(global::Src source)
    {
        var target = new global::Dst();
        target.Foo = (global::TrackedProperty<int?>)source.Foo;
        return target;
    }
}

Environment (please complete the following information):

  • Mapperly Version: 3.6.0/4.0.0-next.3
  • Nullable reference types: enabled
  • .NET Version: .net framework 4.8/.net 8
  • Target Framework: .net standard 2.0/.net framework 4.8/.net 8
  • Compiler Version: 4.11.0-3.24365.8 (9e9c7c1d)
  • C# Language Version: 12
  • IDE: Visual Studio v17.10.5/Rider 2024.2.4
  • OS: Windows 11

Additional context We have a partial workaround by declaring a user-implemented mapping, however we seem to be unable to create a generic user-implemented mapper.

    // This does not work
    public static TrackedProperty<T> UserMap<T>(T source) => source;

    // This does, but requires a separate function per type
    public static TrackedProperty<int?> UserMap(int? source) => source;

MatthewSmit-Scope avatar Sep 19 '24 23:09 MatthewSmit-Scope

Is this issue in progress? If not I can handle with that

MRmlik12 avatar Oct 01 '24 20:10 MRmlik12

Not yet in progress, feel free to contribute.

latonz avatar Oct 02 '24 05:10 latonz

I have just analysed this: It doesn't seem to be related to nullability, but to generics. Mapperly does not support open generic mappings yet. Closing in favour of https://github.com/riok/mapperly/issues/1285.

latonz avatar Nov 07 '24 08:11 latonz