Roslyn: IDE0041 Null check can be simplified is shown for Reference Equals of all generic parameters

Created on 5 Dec 2017  路  22Comments  路  Source: dotnet/roslyn

IDE0041 should only be shown if the type is nullable or the generic parameter is constrained to a nullable type.
Executing the codefix will create invalid code.

Version Used:
Visual Studio Enterprise 2017 15.5 RTM
Steps to Reproduce:

  1. Use repro snippet from below
  2. Build
  3. Look at messages

Expected Behavior:
Not shown
Actual Behavior:
Message: IDE0041 Null check can be simplified is shown for the Reference Equals

Repro:

public static void NotNull<T>(T value, string parameterName)
{
    if (ReferenceEquals(value, null)) // IDE0041 not valid here
    {
        throw new ArgumentNullException(parameterName);
    }
}
Area-IDE Bug Resolution-Fixed

Most helpful comment

I believe this is fixed in 15.9

Nope, just tested 15.9.0 and it is still the same. Also, the auto-correction of the suggested fix does nothing, it leaves the code untouched, even for the simplest cases. It currently says "is null" and not "== null".

image

Edit: it seems that negation of the message doesn't work either, it doesn't say "is not null", nor "!= null":

image

And considering the discussion above, I am not sure whether replacing it with null == other is actually the preferred edit here, but I have to admit that I am not aware of the full set of changes of C# 8, let alone whether changing this would hamper if the same code gets compiled with C# 7 as well.

All 22 comments

My apologies for asking a question on an old issue, but I have recently run into this on VS Professional 15.8.5. I see that it should have been addressed in 15.7. Is that accurate? If so, there may have been a regression.

The following code still has suggestions for IDE0041 in all places (including the original repro above):

    public sealed class A
    {
        public bool Test<T>(T a) => ReferenceEquals(a, null); // IDE0041 not valid here

        public static void NotNull<T>(T value, string parameterName)
        {
            if (ReferenceEquals(value, null)) // IDE0041 not valid here
            {
                throw new ArgumentNullException(parameterName);
            }
        }
    }

    public class B<T>
    {
        public bool Test(T b) => ReferenceEquals(b, null); // IDE0041 not valid here
    }

I also see this in 15.8.3, with a non-generic reference type.

@sharwell I see it too. Can you reopen this?

cc @MaStr11

For unconstrained generic parameters, we should offer to simplify to == null (instead of the invalid is null). See the test here:

https://github.com/dotnet/roslyn/blob/989222085cc9dc4cdb3dd4a9fcd0297d5a61a308/src/EditorFeatures/CSharpTest/UseIsNullCheck/UseIsNullCheckForReferenceEqualsTests.cs#L223-L248

This behavior was discussed on the PR that fixed this issue:
https://github.com/dotnet/roslyn/pull/24173#issuecomment-356917756

It seems that IDE0041 is properly reported, but the CodeAction to do the fix does nothing:

2018-10-03 12_23_20-window

This might already be fixed by @CyrusNajmabadi in commit Fix conflict between fixers that was preventing one from showing up. https://github.com/dotnet/roslyn/commit/856932a1b921764959af35e5f93a4d13909f79f8#diff-f2c45bae9d7dfdbb5253223b6ecfdb48

because if I add the code from above to a test in master https://github.com/MaStr11/roslyn/commit/77c29cdfc406f601e554eea010e7754fdc3a94c4 the test is green (it does the right thing, without any other changes). @CyrusNajmabadi Can you confirm that the current behavior in 15.8.5 is already fixed in master?

PS: It seems the special caseing (== null instead of is null) can be removed soon because #25995 is merged and fixes dotnet/csharplang#1284.

This might already be fixed by @CyrusNajmabadi in commit Fix conflict between fixers that was preventing one from showing up. 856932a#diff-f2c45bae9d7dfdbb5253223b6ecfdb48

You're right, I just checked and this is fixed in master. Although it's a little weird that the code fix title says "Use 'is null' check". In my opinion it should say "Use '== null' check" here.

image

PS: It seems the special caseing (== null instead of is null) can be removed soon because #25995 is merged and fixes dotnet/csharplang#1284.

I'm assuming that the language feature would only be enabled for C# 8. If that's the case, this logic cannot be removed. It still has to work this way if somebody has LangVersion set to 7.3 or below.

Thanks for checking, that it is fixed in master. I couldn't check it myself because I have trouble getting my Roslyn experimental hive working (once more).

In my opinion it should say "Use '== null' check" here.

That's right. I think we did so because this is temporary until C# 8 is released and it's an edge case that doesn't come up that often. The title already distinguishes between is null and not is null and could be extended to also support == and !=.

https://github.com/dotnet/roslyn/blob/989222085cc9dc4cdb3dd4a9fcd0297d5a61a308/src/Features/Core/Portable/UseIsNullCheck/AbstractUseIsNullForReferenceEqualsCodeFixProvider.cs#L40

I'm assuming that the language feature would only be enabled for C# 8. If that's the case, this logic cannot be removed. It still has to work this way if somebody has LangVersion set to 7.3 or below.

Right.

I think we did so because this is temporary until C# 8 is released

It is "sort of" temporary. C# 7 will not change and many users will (as always) continue using projects that have to support building in VS 2017, or even 2015 and avoid using C# 8 for a few years. It would be nice if the title was fixed, because this special case is not going to get removed any time soon.

Closing this as the original bug was still fixed as described. Regressions can be opened as a new issue. Otherwise it makes a mess of the history and links between PRs and issues.

PS: It seems the special caseing (== null instead of is null) can be removed soon ...

I believe this would need a check for the LangVersion property of the project, but that's pretty normal by now.

You're right, I just checked and this is fixed in master.

Based on this, I don't think we need to create a new issue, but we can if people want.

I'm fine with this case having a slightly incorrect title. :)

@CyrusNajmabadi Can you confirm that the current behavior in 15.8.5 is already fixed in master?

It works for me in master. So things seem fine :)

Still not fixed in 15.8.7:

Several samples:

        public T Value
        {
            get
            {
                var some = this as Some;
                Code.AssertState(!ReferenceEquals(some, null), "Option has no value."); // IDE0041
                return some.Value;
            }
        }

        public bool Equals(Option<T> other)
        {
            if (other == null)
                return false;

            var thisSome  = this as Some;
            var otherSome = other as Some;

            if (ReferenceEquals(thisSome, null)) // IDE0041
                return ReferenceEquals(otherSome, null); // IDE0041

            if (ReferenceEquals(otherSome, null)) // IDE0041
                return false;

            return EqualityComparer<T>.Default.Equals(Value, other.Value);
        }

Yep I'm getting the same

Looks like we need a new issue and a proper explanation of the situation

What is Some? Can you please make a full code example?

I believe this is fixed in 15.9

I believe this is fixed in 15.9

Nope, just tested 15.9.0 and it is still the same. Also, the auto-correction of the suggested fix does nothing, it leaves the code untouched, even for the simplest cases. It currently says "is null" and not "== null".

image

Edit: it seems that negation of the message doesn't work either, it doesn't say "is not null", nor "!= null":

image

And considering the discussion above, I am not sure whether replacing it with null == other is actually the preferred edit here, but I have to admit that I am not aware of the full set of changes of C# 8, let alone whether changing this would hamper if the same code gets compiled with C# 7 as well.

@abelbraaksma the correct thing is to do nothing here. At least we do not apply the codefix. We should update the bug to say that the problem is the codefix being offered at all. Looks like someone added some logic so it wasn't applied anymore

@CyrusNajmabadi looks like this was regressed in 15.9

Works for me on the latest bits:

image

I can see the fix isn't in 15.9: https://github.com/dotnet/roslyn/blob/dev15.9.x/src/Features/Core/Portable/UseIsNullCheck/AbstractUseIsNullForReferenceEqualsCodeFixProvider.cs

Specifically, it's missing these lines: https://github.com/dotnet/roslyn/blob/76c02fedee3847cc460311ec19adc737b8580006/src/Features/Core/Portable/UseIsNullCheck/AbstractUseIsNullForReferenceEqualsCodeFixProvider.cs#L31-L32

So this is simply a case of https://github.com/dotnet/roslyn/pull/29458 not being included in 15.9 for some reason.

@jmarolf I don't know how i can help here. The bug has been fixed. Roslyn has just chosen to not release it yet. Let me know if you need any more info. Thanks!

Closing out. Have validated this is working in the 16.x line.

Was this page helpful?
0 / 5 - 0 ratings