dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.06k stars 4.04k forks source link

Nullable Warning when custom Equality Operator is used to generate an Expression Tree #71603

Open csharper2010 opened 10 months ago

csharper2010 commented 10 months ago

Version:

net8.0 or any other version with Nullable Reference Typ support

Steps to Reproduce:

  1. Implement custom equality operators to generate expression tree
  2. Build expression with comparison to null and another branch where the lhs is used on non-nullable target (see gist)
  3. Nullable warning is shown for the second branch

Expected Behavior:

As there is no real null check but a custom operator that generates an expression tree for convenience, flow control should not assume that v1 is null in second branch as it is already not null in the method signature. I am not sure under what conditions the compiler should decide that but as a developer I would say, it's obvious in my case that I don't want that warning.

Actual Behavior:

Compiler shows error "CS8604") on second branch because it seems to assume that the first branch performs a real null check.

Possible workarounds?

Is there a workaround besides disabling nullable context on the operator definitions? Disable is not a good solution as I would like to have the nullable annotations also there.

Complete example also in place

public class Condition {
    public static Condition operator |(Condition left, Condition right) => new Condition();
    public static Condition operator &(Condition left, Condition right) => new Condition();
}

public class Value {}

// Type defines operator == or operator != but does not override Object.Equals(object o)
#pragma warning disable CS0660,CS0661 
public class Path {
#pragma warning restore CS0660,CS0661
    public static Condition operator ==(Path left, Value? right) => new Condition();
    public static Condition operator !=(Path left, Value? right) => new Condition();
    public static Condition operator >(Path left, Value? right) => new Condition();
    public static Condition operator <(Path left, Value? right) => new Condition();
}

public class Test {
    public Condition WrongWarning(Path v1, Value? v2) {
        return v1 == null | v1 > v2;
    }
}
m-gasser commented 8 months ago

I just stumbled over this issue. As a workaround, putting #nullable disable around the overloaded operators works fine in our case.

csharper2010 commented 8 months ago

I just stumbled over this issue. As a workaround, putting #nullable disable around the overloaded operators works fine in our case.

Yep, but "works fine" is quite a relative rating.

It means that nullable annotations don't work on those operators. See

Possible workarounds?

Is there a workaround besides disabling nullable context on the operator definitions? Disable is not a good solution as I would like to have the nullable annotations also there.