File: PreferNullLiteralTests.cs
Web Access
Project: src\src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\UnitTests\Roslyn.Diagnostics.Analyzers.UnitTests.csproj (Roslyn.Diagnostics.Analyzers.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Roslyn.Diagnostics.CSharp.Analyzers.PreferNullLiteral,
    Roslyn.Diagnostics.CSharp.Analyzers.PreferNullLiteralCodeFixProvider>;
 
namespace Roslyn.Diagnostics.Analyzers.UnitTests
{
    public class PreferNullLiteralTests
    {
        [Theory]
        [InlineData("default")]
        [InlineData("default(object)")]
        public async Task PreferNullLiteral_ClassAsync(string defaultValueExpression)
        {
            var source = $@"
class Type
{{
    object Method()
    {{
        return [|{defaultValueExpression}|];
    }}
}}
";
            var fixedSource = @"
class Type
{
    object Method()
    {
        return null;
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, fixedSource);
        }
 
        [Fact]
        public async Task UnresolvedTypeAsync()
        {
            var source = @"
class Type
{
    void Method()
    {
        {|CS0411:Method2|}(default);
    }
 
    void Method2<T>(T value)
    {
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Theory]
        [InlineData("default", "null")]
        [InlineData("default(object)", "null")]
        [InlineData("default(object?)", "null")]
        public async Task ReturnFromNullableContextAsync(string defaultValueExpression, string fixedExpression)
        {
            var source = $@"
#nullable enable
 
class Type
{{
    object Method()
    {{
        return [|{defaultValueExpression}|]!;
    }}
}}
";
            var fixedSource = $@"
#nullable enable
 
class Type
{{
    object Method()
    {{
        return {fixedExpression}!;
    }}
}}
";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                LanguageVersion = LanguageVersion.CSharp8,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("[|default(object)|]!", "((object?)null)!")]
        [InlineData("[|default(object?)|]!", "((object?)null)!")]
        [InlineData("[|default(object)|]", "(object?)null")]
        [InlineData("[|default(object?)|]", "(object?)null")]
        public async Task InvocationInNullableContextAsync(string defaultValueExpression, string fixedExpression)
        {
            var source = $@"
#nullable enable
 
class Type
{{
    void Method()
    {{
        Method2({defaultValueExpression});
    }}
 
    void Method2<T>(T value)
    {{
    }}
}}
";
            var fixedSource = $@"
#nullable enable
 
class Type
{{
    void Method()
    {{
        Method2({fixedExpression});
    }}
 
    void Method2<T>(T value)
    {{
    }}
}}
";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                LanguageVersion = LanguageVersion.CSharp8,
            }.RunAsync();
        }
 
        [Fact]
        public async Task NullPointerAsync()
        {
            var source = @"
unsafe class Type
{
    void Method()
    {
        Method2([|default(int*)|]);
    }
 
    void Method2(int* value) { }
    void Method2(byte* value) { }
}
";
            var fixedSource = @"
unsafe class Type
{
    void Method()
    {
        Method2((int*)null);
    }
 
    void Method2(int* value) { }
    void Method2(byte* value) { }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, fixedSource);
        }
 
        [Fact]
        public async Task PointerInNullableContextAsync()
        {
            var source = @"
#nullable enable
 
unsafe class Type
{
    void Method()
    {
        Method2([|default(int*)|]);
    }
 
    void Method2(int* value) { }
    void Method2(byte* value) { }
}
";
            var fixedSource = @"
#nullable enable
 
unsafe class Type
{
    void Method()
    {
        Method2((int*)null);
    }
 
    void Method2(int* value) { }
    void Method2(byte* value) { }
}
";
 
            await new VerifyCS.Test
            {
                TestCode = source,
                FixedCode = fixedSource,
                LanguageVersion = LanguageVersion.CSharp8,
            }.RunAsync();
        }
 
        [Theory]
        [InlineData("default")]
        [InlineData("default(object)")]
        public async Task PreferNullLiteral_DefaultParameterValueAsync(string defaultValueExpression)
        {
            var source = $@"
class Type
{{
    void Method(object value = [|{defaultValueExpression}|])
    {{
    }}
}}
";
            var fixedSource = @"
class Type
{
    void Method(object value = null)
    {
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, fixedSource);
        }
 
        [Fact]
        public async Task PreferNullLiteral_ArgumentFormattingAsync()
        {
            var source = $@"
class Type
{{
    void Method()
    {{
        Method2(
            0,
            [|default|],
            /*1*/ [|default|] /*2*/,
            [|default(object)|],
            /*1*/ [|default /*2*/ ( /*3*/ object /*4*/ )|] /*5*/,
            """");
    }}
 
    void Method2(params object[] values)
    {{
    }}
}}
";
            var fixedSource = @"
class Type
{
    void Method()
    {
        Method2(
            0,
            null,
            /*1*/ null /*2*/,
            null,
            /*1*/  /*3*/  /*4*/ null /*2*/  /*5*/,
            """");
    }
 
    void Method2(params object[] values)
    {
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, fixedSource);
        }
 
        [Fact]
        public async Task PreferNullLiteral_OverloadResolutionAsync()
        {
            var source = @"
using System;
 
class Type
{
    void Method()
    {
        Method2([|default(object)|]);
        Method2([|default(string)|]);
        Method2([|default(IComparable)|]);
        Method2([|default(int?)|]);
        Method2(default(int));
    }
 
    void Method2<T>(T value)
    {
    }
}
";
            var fixedSource = @"
using System;
 
class Type
{
    void Method()
    {
        Method2((object)null);
        Method2((string)null);
        Method2((IComparable)null);
        Method2((int?)null);
        Method2(default(int));
    }
 
    void Method2<T>(T value)
    {
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, fixedSource);
        }
 
        [Fact]
        public async Task PreferNullLiteral_ParenthesizeWhereNecessaryAsync()
        {
            var source = @"
using System;
 
class Type
{
    void Method()
    {
        Method2([|default(object)|]?.ToString());
        Method2([|default(string)|]?.ToString());
        Method2([|default(IComparable)|]?.ToString());
        Method2([|default(int?)|]?.ToString());
        Method2(default(int).ToString());
    }
 
    void Method2(string value)
    {
    }
}
";
            var fixedSource = @"
using System;
 
class Type
{
    void Method()
    {
        Method2(((object)null)?.ToString());
        Method2(((string)null)?.ToString());
        Method2(((IComparable)null)?.ToString());
        Method2(((int?)null)?.ToString());
        Method2(default(int).ToString());
    }
 
    void Method2(string value)
    {
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, fixedSource);
        }
 
        [Fact]
        public async Task PreferNullLiteral_StructAsync()
        {
            var source = @"
class Type
{
    int Method()
    {
        return default;
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public async Task PreferNullLiteral_StructConvertedToReferenceTypeAsync()
        {
            var source = @"
class Type
{
    object Method()
    {
        return default(int);
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public async Task PreferNullLiteral_UnconstrainedGenericAsync()
        {
            var source = @"
class Type
{
    T Method<T>()
    {
        return default;
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public async Task PreferNullLiteral_GenericConstrainedToReferenceTypeAsync()
        {
            var source = @"
class Type
{
    T Method<T>()
        where T : class
    {
        return [|default|];
    }
}
";
            var fixedSource = @"
class Type
{
    T Method<T>()
        where T : class
    {
        return null;
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, fixedSource);
        }
 
        [Fact]
        public async Task PreferNullLiteral_GenericConstrainedToInterfaceAsync()
        {
            var source = @"
class Type
{
    T Method<T>()
        where T : System.IComparable
    {
        return default;
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Fact]
        public async Task PreferNullLiteral_GenericConstrainedToValueTypeAsync()
        {
            var source = @"
class Type
{
    T Method<T>()
        where T : struct
    {
        return default;
    }
}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, source);
        }
 
        [Theory]
        [InlineData("object")]
        [InlineData("int?")]
        public async Task IgnoreDefaultParametersAsync(string defaultParameterType)
        {
            var source = $@"
class Type
{{
    void Method1()
    {{
        Method2(0);
    }}
 
    void Method2(int first, {defaultParameterType} value = null)
    {{
    }}
}}
";
 
            await VerifyCS.VerifyCodeFixAsync(source, source);
        }
    }
}