Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect nullable handling with implicit operators #1500

Open
MatthewSmit-Scope opened this issue Sep 19, 2024 · 2 comments
Open

Incorrect nullable handling with implicit operators #1500

MatthewSmit-Scope opened this issue Sep 19, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@MatthewSmit-Scope
Copy link

MatthewSmit-Scope commented Sep 19, 2024

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 MatthewSmit-Scope added the bug Something isn't working label Sep 19, 2024
@MRmlik12
Copy link

MRmlik12 commented Oct 1, 2024

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

@latonz
Copy link
Contributor

latonz commented Oct 2, 2024

Not yet in progress, feel free to contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants