File: Microsoft.NetCore.Analyzers\Usage\DoNotCompareSpanToNullTests.cs
Web Access
Project: ..\..\..\src\Microsoft.CodeAnalysis.NetAnalyzers\tests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.csproj (Microsoft.CodeAnalysis.NetAnalyzers.UnitTests)
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the MIT license.  See License.txt in the project root for license information.
 
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.NetCore.CSharp.Analyzers.Usage;
using Microsoft.NetCore.VisualBasic.Analyzers.Tasks;
using Test.Utilities;
using Xunit;
 
namespace Microsoft.NetCore.Analyzers.Usage.UnitTests
{
    using VerifyCS = CSharpCodeFixVerifier<DoNotCompareSpanToNullAnalyzer, CSharpDoNotCompareSpanToNullFixer>;
    using VerifyVB = VisualBasicCodeFixVerifier<DoNotCompareSpanToNullAnalyzer, BasicDoNotCompareSpanToNullFixer>;
 
    public sealed class DoNotCompareSpanToNullTests
    {
        private const string CSharpClass = """
                                           using System;
                                           using System.Diagnostics;
 
                                           public class Test
                                           {{
                                               public void Run({0} span)
                                               {{
                                                   {1}
                                               }}
                                           }}
                                           """;
 
        private const string VbClass = """
                                       Imports System
                                       Imports System.Diagnostics
 
                                       Public Class Test
                                           <Obsolete>
                                           Public Sub Run(span As {0})
                                               {1}
                                           End Sub
                                       End Class
                                       """;
 
        private static readonly DiagnosticResult DoNotCompareToNullResult = new DiagnosticResult(DoNotCompareSpanToNullAnalyzer.DoNotCompareSpanToNullRule).WithLocation(0);
        private static readonly DiagnosticResult DoNotCompareToDefaultResult = new DiagnosticResult(DoNotCompareSpanToNullAnalyzer.DoNotCompareSpanToDefaultRule).WithLocation(0);
 
        [Fact]
        public async Task CheckInIf_Diagnostic()
        {
            await VerifyCsharpCompareToNullAsync("if ({|#0:span == null|}) {}", "if (span.IsEmpty) {}");
            await VerifyCsharpCompareToDefaultAsync("if ({|#0:span == default|}) {}", "if (span.IsEmpty) {}");
 
            await VerifyVisualBasicAsync("If {|#0:span = Nothing|} Then\nEnd If", "If span.IsEmpty Then\nEnd If");
        }
 
        [Fact]
        public async Task NegatedCheckInIf_Diagnostic()
        {
            await VerifyCsharpCompareToNullAsync("if ({|#0:span != null|}) {}", "if (!span.IsEmpty) {}");
            await VerifyCsharpCompareToDefaultAsync("if ({|#0:span != default|}) {}", "if (!span.IsEmpty) {}");
 
            await VerifyVisualBasicAsync("If {|#0:span <> Nothing|} Then\nEnd If", "If Not span.IsEmpty Then\nEnd If");
        }
 
        [Fact]
        public async Task BooleanDeclaration_Diagnostic()
        {
            await VerifyCsharpCompareToNullAsync("var x = {|#0:span == null|};", "var x = span.IsEmpty;");
            await VerifyCsharpCompareToDefaultAsync("var x = {|#0:span == default|};", "var x = span.IsEmpty;");
 
            await VerifyVisualBasicAsync("Dim x = {|#0:span = Nothing|}", "Dim x = span.IsEmpty");
        }
 
        [Fact]
        public async Task WhenComparisonIsUsedAsArgument_Diagnostic()
        {
            await VerifyCsharpCompareToNullAsync("Debug.Assert({|#0:span == null|});", "Debug.Assert(span.IsEmpty);");
            await VerifyCsharpCompareToDefaultAsync("Debug.Assert({|#0:span == default|});", "Debug.Assert(span.IsEmpty);");
            await VerifyCsharpCompareToNullAsync("Debug.Assert({|#0:span != null|});", "Debug.Assert(!span.IsEmpty);");
            await VerifyCsharpCompareToDefaultAsync("Debug.Assert({|#0:span != default|});", "Debug.Assert(!span.IsEmpty);");
 
            await VerifyVisualBasicAsync("Debug.Assert({|#0:span = Nothing|})", "Debug.Assert(span.IsEmpty)");
            await VerifyVisualBasicAsync("Debug.Assert({|#0:span <> Nothing|})", "Debug.Assert(Not span.IsEmpty)");
        }
 
        [Fact]
        public async Task CompareWithDefault_Diagnostic()
        {
            await VerifyCsharpCompareToDefaultAsync("var x = {|#0:span == default|};", "var x = span.IsEmpty;");
            await VerifyCsharpCompareToDefaultAsync("var x = {|#0:span == default(Span<int>)|};", "var x = span.IsEmpty;");
        }
 
        [Fact]
        public async Task IsEmpty_NoDiagnostic()
        {
            await VerifyNoDiagnosticCsharpAsync("var x = span.IsEmpty;");
 
            await VerifyNoDiagnosticVisualBasicAsync("Dim x = span.IsEmpty");
        }
 
        [Theory]
        [InlineData("Span<int>", "Span(Of Int32)")]
        [InlineData("ReadOnlySpan<int>", "ReadOnlySpan(Of Int32)")]
        public async Task CompareToOtherSpan_NoDiagnostic(string csType, string vbType)
        {
            var csharpCode = $"""
                              {csType} otherSpan = stackalloc int[0];
                              var x = span == otherSpan;
                              """;
            var vbCode = $"""
                          Dim otherSpan As {vbType} = Nothing
                          Dim x = span = otherSpan
                          """;
            await VerifyNoDiagnosticCsharpAsync(csharpCode);
            await VerifyNoDiagnosticVisualBasicAsync(vbCode);
        }
 
        [Fact]
        public async Task CheckOnLeftSide_Diagnostic()
        {
            await VerifyCsharpCompareToNullAsync("var x = {|#0:null == span|};", "var x = span.IsEmpty;");
            await VerifyCsharpCompareToDefaultAsync("var x = {|#0:default == span|};", "var x = span.IsEmpty;");
            await VerifyCsharpCompareToDefaultAsync("var x = {|#0:default(Span<int>) == span|};", "var x = span.IsEmpty;");
 
            await VerifyVisualBasicAsync("Dim x = {|#0:Nothing = span|}", "Dim x = span.IsEmpty");
        }
 
        [Fact]
        public async Task NegatedCheckOnLeftSide_Diagnostic()
        {
            await VerifyCsharpCompareToNullAsync("var x = {|#0:null != span|};", "var x = !span.IsEmpty;");
            await VerifyCsharpCompareToDefaultAsync("var x = {|#0:default != span|};", "var x = !span.IsEmpty;");
            await VerifyCsharpCompareToDefaultAsync("var x = {|#0:default(Span<int>) != span|};", "var x = !span.IsEmpty;");
 
            await VerifyVisualBasicAsync("Dim x = {|#0:Nothing <> span|}", "Dim x = Not span.IsEmpty");
        }
 
        [Theory]
        [CombinatorialData]
        public Task NonSpanNullCheck_NoDiagnostic([CombinatorialValues("string", "HttpClient", "IDictionary<string, int>", "IEnumerable<string>")] string type, [CombinatorialValues("==", "!=", "is", "is not")] string comparison)
        {
            var code = $$"""
                         using System.Collections.Generic;
                         using System.Net.Http;
 
                         public class Test
                         {
                             public void Run({{type}} x)
                             {
                                 var y = x {{comparison}} null;
                             }
                         }
                         """;
 
            return new VerifyCS.Test
            {
                TestCode = code,
                LanguageVersion = LanguageVersion.CSharp9
            }.RunAsync();
        }
 
        [Theory]
        [CombinatorialData]
        public Task Vb_NonSpanNullCheck_NoDiagnostic([CombinatorialValues("String", "HttpClient", "IDictionary(Of String, Int32)", "IEnumerable(Of String)")] string type, [CombinatorialValues("Is", "IsNot")] string comparison)
        {
            var code = $"""
                       Imports System
                       Imports System.Collections.Generic
                       Imports System.Net.Http
 
                       Public Class Test
                           Public Sub Run(x As {type})
                               Dim y = x {comparison} Nothing
                           End Sub
                       End Class
                       """;
 
            return VerifyVB.VerifyAnalyzerAsync(code);
        }
 
        private static async Task VerifyNoDiagnosticCsharpAsync(string code)
        {
            var spanCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "Span<int>", code);
            var rosCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "ReadOnlySpan<int>", code);
 
            await VerifyCS.VerifyAnalyzerAsync(spanCode);
            await VerifyCS.VerifyAnalyzerAsync(rosCode);
        }
 
        private static async Task VerifyCsharpCompareToNullAsync(string code, string fixedCode)
        {
            var spanCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "Span<int>", code);
            var fixedSpanCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "Span<int>", fixedCode);
 
            var rosCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "ReadOnlySpan<int>", code);
            var fixedRosCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "ReadOnlySpan<int>", fixedCode);
 
            await new VerifyCS.Test
            {
                TestCode = spanCode,
                FixedCode = fixedSpanCode,
                ExpectedDiagnostics = { DoNotCompareToNullResult }
            }.RunAsync();
 
            await new VerifyCS.Test
            {
                TestCode = rosCode,
                FixedCode = fixedRosCode,
                ExpectedDiagnostics = { DoNotCompareToNullResult }
            }.RunAsync();
        }
 
        private static async Task VerifyCsharpCompareToDefaultAsync(string code, string fixedCode)
        {
            var spanCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "Span<int>", code);
            var fixedSpanCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "Span<int>", fixedCode);
 
            var rosCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "ReadOnlySpan<int>", code);
            var fixedRosCode = string.Format(CultureInfo.InvariantCulture, CSharpClass, "ReadOnlySpan<int>", fixedCode);
 
            await new VerifyCS.Test
            {
                TestCode = spanCode,
                FixedCode = fixedSpanCode,
                ExpectedDiagnostics = { DoNotCompareToDefaultResult }
            }.RunAsync();
 
            await new VerifyCS.Test
            {
                TestCode = rosCode,
                FixedCode = fixedRosCode,
                ExpectedDiagnostics = { DoNotCompareToDefaultResult }
            }.RunAsync();
        }
 
        private static async Task VerifyNoDiagnosticVisualBasicAsync(string code)
        {
            var spanCode = string.Format(CultureInfo.InvariantCulture, VbClass, "Span(Of Int32)", code);
            var rosCode = string.Format(CultureInfo.InvariantCulture, VbClass, "ReadOnlySpan(Of Int32)", code);
 
            await VerifyVB.VerifyAnalyzerAsync(spanCode);
            await VerifyVB.VerifyAnalyzerAsync(rosCode);
        }
 
        private static async Task VerifyVisualBasicAsync(string code, string fixedCode)
        {
            var spanCode = string.Format(CultureInfo.InvariantCulture, VbClass, "Span(Of Int32)", code);
            var fixedSpanCode = string.Format(CultureInfo.InvariantCulture, VbClass, "Span(Of Int32)", fixedCode);
 
            var rosCode = string.Format(CultureInfo.InvariantCulture, VbClass, "ReadOnlySpan(Of Int32)", code);
            var fixedRosCode = string.Format(CultureInfo.InvariantCulture, VbClass, "ReadOnlySpan(Of Int32)", fixedCode);
 
            await new VerifyVB.Test
            {
                TestCode = spanCode,
                FixedCode = fixedSpanCode,
                ExpectedDiagnostics = { DoNotCompareToNullResult }
            }.RunAsync();
 
            await new VerifyVB.Test
            {
                TestCode = rosCode,
                FixedCode = fixedRosCode,
                ExpectedDiagnostics = { DoNotCompareToNullResult }
            }.RunAsync();
        }
    }
}