File: Microsoft.NetCore.Analyzers\Performance\ConstantExpectedTests.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.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.NetCore.CSharp.Analyzers.Performance.CSharpConstantExpectedAnalyzer,
    Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
 
namespace Microsoft.NetCore.Analyzers.Performance.UnitTests
{
    public sealed class ConstantExpectedTests
    {
        [Theory]
        [InlineData("char", "char.MinValue", "char.MaxValue")]
        [InlineData("sbyte", "sbyte.MinValue", "sbyte.MaxValue")]
        [InlineData("short", "short.MinValue", "short.MaxValue")]
        [InlineData("int", "int.MinValue", "int.MaxValue")]
        [InlineData("long", "long.MinValue", "long.MaxValue")]
        [InlineData("byte", "byte.MinValue", "byte.MaxValue")]
        [InlineData("ushort", "ushort.MinValue", "ushort.MaxValue")]
        [InlineData("uint", "uint.MinValue", "uint.MaxValue")]
        [InlineData("ulong", "ulong.MinValue", "ulong.MaxValue")]
        [InlineData("bool", "false", "true")]
        [InlineData("float", "float.MinValue", "float.MaxValue")]
        [InlineData("double", "double.MinValue", "double.MaxValue")]
        public static async Task TestConstantExpectedSupportedUnmanagedTypesAsync(string type, string minValue, string maxValue)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod1([ConstantExpected] {type} val) {{ }}
    public static void TestMethod2([ConstantExpected(Min={minValue})] {type} val) {{ }}
    public static void TestMethod3([ConstantExpected(Max={maxValue})] {type} val) {{ }}
    public static void TestMethod4([ConstantExpected(Min={minValue}, Max={maxValue})] {type} val) {{ }}
    public static void TestMethod5([ConstantExpected(Min=null)] {type} val) {{ }}
    public static void TestMethod6([ConstantExpected(Max=null)] {type} val) {{ }}
    public static void TestMethod7([ConstantExpected(Min=null, Max=null)] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("sbyte", "sbyte.MinValue", "sbyte.MaxValue")]
        [InlineData("short", "short.MinValue", "short.MaxValue")]
        [InlineData("int", "int.MinValue", "int.MaxValue")]
        [InlineData("long", "long.MinValue", "long.MaxValue")]
        [InlineData("byte", "byte.MinValue", "byte.MaxValue")]
        [InlineData("ushort", "ushort.MinValue", "ushort.MaxValue")]
        [InlineData("uint", "uint.MinValue", "uint.MaxValue")]
        [InlineData("ulong", "ulong.MinValue", "ulong.MaxValue")]
        public static async Task TestConstantExpectedSupportedEnumTypesAsync(string type, string minValue, string maxValue)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod1([ConstantExpected] AEnum val) {{ }}
    public static void TestMethod2([ConstantExpected(Min={minValue})] AEnum val) {{ }}
    public static void TestMethod3([ConstantExpected(Max={maxValue})] AEnum val) {{ }}
    public static void TestMethod4([ConstantExpected(Min={minValue}, Max={maxValue})] AEnum val) {{ }}
    public static void TestMethod5([ConstantExpected(Min=null)] AEnum val) {{ }}
    public static void TestMethod6([ConstantExpected(Max=null)] AEnum val) {{ }}
    public static void TestMethod7([ConstantExpected(Min=null, Max=null)] AEnum val) {{ }}
}}
 
public enum AEnum : {type}
{{
    One,
    Two
}}
";
            await TestCSAsync(csInput);
        }
 
        [Fact]
        public static async Task TestConstantExpectedSupportedComplexTypesAsync()
        {
            string csInput = @"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{
    public static void TestMethodString([ConstantExpected] string val) { }
    public static void TestMethodGeneric<T>([ConstantExpected] T val) { }
    
    public static class GenenricClass<T>
    {
        public static void TestMethodGeneric([ConstantExpected] T val) { }
    }
}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("char")]
        [InlineData("sbyte")]
        [InlineData("short")]
        [InlineData("int")]
        [InlineData("long")]
        [InlineData("byte")]
        [InlineData("ushort")]
        [InlineData("uint")]
        [InlineData("ulong")]
        [InlineData("float")]
        [InlineData("double")]
        [InlineData("bool")]
        [InlineData("string")]
        public static async Task TestConstantExpectedSupportedComplex2TypesAsync(string type)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public interface ITest<T>
    {{
        T Method(T operand1, [ConstantExpected] T operand2);
    }}
    public interface ITest2<T>
    {{
        T Method(T operand1, [ConstantExpected] T operand2);
    }}
    public abstract class AbstractTest<T>
    {{
        public abstract T Method2(T operand1, [ConstantExpected] T operand2);
    }}
 
    public class Generic : AbstractTest<{type}>, ITest<{type}>, ITest2<{type}>
    {{
        public {type} Method({type} operand1, {{|#0:{type} operand2|}}) => throw new NotImplementedException();
        {type} ITest2<{type}>.Method({type} operand1, {{|#1:{type} operand2|}}) => throw new NotImplementedException();
        public override {type} Method2({type} operand1, {{|#2:{type} operand2|}}) => throw new NotImplementedException();
    }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.AttributeExpectedRule)
                        .WithLocation(0),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.AttributeExpectedRule)
                        .WithLocation(1),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.AttributeExpectedRule)
                        .WithLocation(2));
        }
 
        [Theory]
        [InlineData("char")]
        [InlineData("sbyte")]
        [InlineData("short")]
        [InlineData("int")]
        [InlineData("long")]
        [InlineData("byte")]
        [InlineData("ushort")]
        [InlineData("uint")]
        [InlineData("ulong")]
        [InlineData("float")]
        [InlineData("double")]
        [InlineData("bool")]
        [InlineData("string")]
        public static async Task TestMissingConstantExpectedSupportedComplex2TypesAsync(string type)
        {
            string csInput = @$"
using System;
using Similar;
#nullable enable
 
public class Test
{{
    public interface ITest<T>
    {{
        T Method(T operand1, [ConstantExpected] T operand2);
    }}
    public interface ITest2<T>
    {{
        T Method(T operand1, [ConstantExpected] T operand2);
    }}
    public abstract class AbstractTest<T>
    {{
        public abstract T Method2(T operand1, [ConstantExpected] T operand2);
    }}
 
    public class Generic : AbstractTest<{type}>, ITest<{type}>, ITest2<{type}>
    {{
        public {type} Method({type} operand1, {type} operand2) => throw new NotImplementedException();
        {type} ITest2<{type}>.Method({type} operand1, {type} operand2) => throw new NotImplementedException();
        public override {type} Method2({type} operand1, {type} operand2) => throw new NotImplementedException();
    }}
}}
";
            await TestCSMissingAttributeAsync(csInput);
        }
 
        [Fact]
        public static async Task TestConstantExpectedSupportedComplex3TypesAsync()
        {
            string csInput = @"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{
    public interface ITest<T>
    {
        T Method(T operand1, [ConstantExpected] T operand2);
    }
    public interface ITest2<T>
    {
        T Method(T operand1, [ConstantExpected] T operand2);
    }
    public abstract class AbstractTest<T>
    {
        public abstract T Method2(T operand1, [ConstantExpected] T operand2);
    }
    public class GenericForward<T> : AbstractTest<T>, ITest<T>, ITest2<T>
    {
        public T Method(T operand1, {|#0:T operand2|}) => throw new NotImplementedException();
        T ITest2<T>.Method(T operand1, {|#1:T operand2|}) => throw new NotImplementedException();
        public override T Method2(T operand1, {|#2:T operand2|}) => throw new NotImplementedException();
    }
}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.AttributeExpectedRule)
                        .WithLocation(0),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.AttributeExpectedRule)
                        .WithLocation(1),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.AttributeExpectedRule)
                        .WithLocation(2));
        }
 
        [Theory]
        [InlineData("", "", "nint", "nint")]
        [InlineData("", "", "nuint", "nuint")]
        [InlineData("", "", "object", "object")]
        [InlineData("", "", "Test", "Test")]
        [InlineData("", "", "Guid", "System.Guid")]
        [InlineData("", "", "decimal", "decimal")]
        [InlineData("", "", "byte[]", "byte[]")]
        [InlineData("", "", "(int, long)", "(int, long)")]
        [InlineData("<T>", "", "T[]", "T[]")]
        [InlineData("", "<T>", "T[]", "T[]")]
        public static async Task TestConstantExpectedUnsupportedTypesAsync(string classGeneric, string methodGeneric, string type, string diagnosticType)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test{classGeneric}
{{
    public static void TestMethod{methodGeneric}([{{|#0:ConstantExpected|}}] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.UnsupportedTypeRule)
                        .WithLocation(0)
                        .WithArguments(diagnosticType));
        }
 
        [Theory]
        [InlineData("object")]
        [InlineData("Test")]
        [InlineData("Guid")]
        [InlineData("decimal")]
        [InlineData("byte[]")]
        [InlineData("(int, long)")]
        public static async Task TestConstantExpectedUnsupportedIgnoredComplexTypesAsync(string type)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public interface ITest<T>
    {{
        T Method(T operand1, [ConstantExpected] T operand2);
    }}
    public interface ITest2<T>
    {{
        T Method(T operand1, [ConstantExpected] T operand2);
    }}
    public abstract class AbstractTest<T>
    {{
        public abstract T Method2(T operand1, [ConstantExpected] T operand2);
    }}
    public class Generic : AbstractTest<{type}>, ITest<{type}>, ITest2<{type}>
    {{
        public {type} Method({type} operand1, {type} operand2) => throw new NotImplementedException();
        {type} ITest2<{type}>.Method({type} operand1, {type} operand2) => throw new NotImplementedException();
        public override {type} Method2({type} operand1, {type} operand2) => throw new NotImplementedException();
    }}
}}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("", "", "char", "\"a\"", "\"a\"")]
        [InlineData("", "", "sbyte", "\"a\"", "\"a\"")]
        [InlineData("", "", "short", "\"a\"", "\"a\"")]
        [InlineData("", "", "int", "\"a\"", "\"a\"")]
        [InlineData("", "", "long", "\"a\"", "\"a\"")]
        [InlineData("", "", "byte", "\"a\"", "\"a\"")]
        [InlineData("", "", "ushort", "\"a\"", "\"a\"")]
        [InlineData("", "", "uint", "\"a\"", "\"a\"")]
        [InlineData("", "", "ulong", "\"a\"", "\"a\"")]
        [InlineData("", "", "bool", "\"a\"", "\"a\"")]
        [InlineData("", "", "float", "\"a\"", "\"a\"")]
        [InlineData("", "", "double", "\"a\"", "\"a\"")]
        [InlineData("", "", "string", "true", "false")]
        [InlineData("<T>", "", "T", "\"min\"", "false")]
        [InlineData("", "<T>", "T", "\"min\"", "false")]
        public static async Task TestConstantExpectedIncompatibleConstantTypeErrorAsync(string classGeneric, string methodGeneric, string type, string badMinValue, string badMaxValue)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test{classGeneric}
{{
    public static void TestMethod{methodGeneric}([ConstantExpected({{|#0:Min = {badMinValue}|}})] {type} val) {{ }}
    public static void TestMethod2{methodGeneric}([ConstantExpected({{|#1:Min = {badMinValue}|}}, {{|#2:Max = {badMaxValue}|}})] {type} val) {{ }}
    public static void TestMethod3{methodGeneric}([ConstantExpected({{|#3:Max = {badMaxValue}|}})] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                        .WithLocation(0)
                        .WithArguments("Min", type),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                        .WithLocation(1)
                        .WithArguments("Min", type),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                        .WithLocation(2)
                        .WithArguments("Max", type),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                        .WithLocation(3)
                        .WithArguments("Max", type));
        }
 
        [Theory]
        [InlineData("char", "'Z'", "'A'")]
        [InlineData("sbyte", "1", "0")]
        [InlineData("short", "1", "0")]
        [InlineData("int", "1", "0")]
        [InlineData("long", "1", "0")]
        [InlineData("byte", "1", "0")]
        [InlineData("ushort", "1", "0")]
        [InlineData("uint", "1", "0")]
        [InlineData("ulong", "1", "0")]
        [InlineData("float", "1", "0")]
        [InlineData("double", "1", "0")]
        public static async Task TestConstantExpectedInvertedConstantTypeErrorAsync(string type, string min, string max)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod([{{|#0:ConstantExpected(Min = {min}, Max = {max})|}}] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvertedRangeRule)
                        .WithLocation(0));
        }
 
        [Theory]
        [InlineData("sbyte", "AEnum.Five", "AEnum.Two")]
        [InlineData("short", "AEnum.Five", "AEnum.Two")]
        [InlineData("int", "AEnum.Five", "AEnum.Two")]
        [InlineData("long", "AEnum.Five", "AEnum.Two")]
        [InlineData("byte", "AEnum.Five", "AEnum.Two")]
        [InlineData("ushort", "AEnum.Five", "AEnum.Two")]
        [InlineData("uint", "AEnum.Five", "AEnum.Two")]
        [InlineData("ulong", "AEnum.Five", "AEnum.Two")]
        public static async Task TestEnumConstantExpectedInvertedConstantTypeErrorAsync(string type, string min, string max)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod([{{|#0:ConstantExpected(Min = {min}, Max = {max})|}}] {type} val) {{ }}
}}
 
public enum AEnum : {type}
{{
    Zero,
    One = 1,
    Two = 1 << 1,
    Three = 1 << 2,
    Four = 1 << 3,
    Five = 1 << 4
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvertedRangeRule)
                        .WithLocation(0));
        }
 
        [Theory]
        [InlineData("sbyte", sbyte.MinValue, sbyte.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("short", short.MinValue, short.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("int", int.MinValue, int.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("long", long.MinValue, long.MaxValue, "ulong.MaxValue", "ulong.MaxValue", "false", "true")]
        [InlineData("byte", byte.MinValue, byte.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("ushort", ushort.MinValue, ushort.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("uint", uint.MinValue, uint.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("ulong", ulong.MinValue, ulong.MaxValue, "long.MinValue", "-1", "false", "true")]
        [InlineData("float", float.MinValue, float.MaxValue, "double.MinValue", "double.MaxValue", "false", "true")]
        public static async Task TestConstantExpectedInvalidBoundsAsync(string type, object min, object max, string min1, string max1, string badMinValue, string badMaxValue)
        {
            string minString = min.ToString();
            string maxString = max.ToString();
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod([ConstantExpected({{|#0:Min = {min1}|}})] {type} val) {{ }}
    public static void TestMethod2([ConstantExpected({{|#1:Min = {min1}|}}, {{|#2:Max = {max1}|}})] {type} val) {{ }}
    public static void TestMethod3([ConstantExpected({{|#3:Max = {max1}|}})] {type} val) {{ }}
    public static void TestMethod4([ConstantExpected({{|#4:Min = {badMinValue}|}}, {{|#5:Max = {max1}|}})] {type} val) {{ }}
    public static void TestMethod5([ConstantExpected({{|#6:Min = {min1}|}}, {{|#7:Max = {badMaxValue}|}})] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(0)
                    .WithArguments("Min", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(1)
                    .WithArguments("Min", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(2)
                    .WithArguments("Max", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(3)
                    .WithArguments("Max", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                    .WithLocation(4)
                    .WithArguments("Min", type),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(5)
                    .WithArguments("Max", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(6)
                    .WithArguments("Min", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                    .WithLocation(7)
                    .WithArguments("Max", type));
        }
 
        [Theory]
        [InlineData("sbyte", sbyte.MinValue, sbyte.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("short", short.MinValue, short.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("int", int.MinValue, int.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("long", long.MinValue, long.MaxValue, "ulong.MaxValue", "ulong.MaxValue", "false", "true")]
        [InlineData("byte", byte.MinValue, byte.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("ushort", ushort.MinValue, ushort.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("uint", uint.MinValue, uint.MaxValue, "long.MinValue", "long.MaxValue", "false", "true")]
        [InlineData("ulong", ulong.MinValue, ulong.MaxValue, "long.MinValue", "-1", "false", "true")]
        public static async Task TestEnumConstantExpectedInvalidBoundsAsync(string type, object min, object max, string min1, string max1, string badMinValue, string badMaxValue)
        {
            string minString = min.ToString();
            string maxString = max.ToString();
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod([ConstantExpected({{|#0:Min = {min1}|}})] AEnum val) {{ }}
    public static void TestMethod2([ConstantExpected({{|#1:Min = {min1}|}}, {{|#2:Max = {max1}|}})] AEnum val) {{ }}
    public static void TestMethod3([ConstantExpected({{|#3:Max = {max1}|}})] AEnum val) {{ }}
    public static void TestMethod4([ConstantExpected({{|#4:Min = {badMinValue}|}}, {{|#5:Max = {max1}|}})] AEnum val) {{ }}
    public static void TestMethod5([ConstantExpected({{|#6:Min = {min1}|}}, {{|#7:Max = {badMaxValue}|}})] AEnum val) {{ }}
}}
 
public enum AEnum : {type}
{{
    Zero,
    One = 1,
    Two = 1 << 1,
    Three = 1 << 2,
    Four = 1 << 3,
    Five = 1 << 4
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(0)
                    .WithArguments("Min", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(1)
                    .WithArguments("Min", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(2)
                    .WithArguments("Max", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(3)
                    .WithArguments("Max", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                    .WithLocation(4)
                    .WithArguments("Min", "AEnum"),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(5)
                    .WithArguments("Max", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.InvalidBoundsRule)
                    .WithLocation(6)
                    .WithArguments("Min", minString, maxString),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1856.IncompatibleConstantTypeRule)
                    .WithLocation(7)
                    .WithArguments("Max", "AEnum"));
        }
 
        [Theory]
        [InlineData("char", "'A'", "'Z'", "'A'", "(char)('A'+'\\u0001')")]
        [InlineData("sbyte", "10", "20", "10", "2*5")]
        [InlineData("short", "10", "20", "10", "2*5")]
        [InlineData("int", "10", "20", "10", "2*5")]
        [InlineData("long", "10", "20", "10", "2*5")]
        [InlineData("byte", "10", "20", "10", "2*5")]
        [InlineData("ushort", "10", "20", "10", "2*5")]
        [InlineData("uint", "10", "20", "10", "2*5")]
        [InlineData("ulong", "10", "20", "10", "2*5")]
        [InlineData("float", "10", "20", "10", "2*5")]
        [InlineData("double", "10", "20", "10", "2*5")]
        [InlineData("bool", "true", "true", "true", "!false")]
        [InlineData("string", "null", "null", "\"true\"", "\"false\"")]
        public static async Task TestArgumentConstantAsync(string type, string minValue, string maxValue, string value, string expression)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod()
    {{
        TestMethodWithConstant({value});
        TestMethodWithConstant({expression});
        TestMethodWithConstrainedConstant({value});
        TestMethodWithConstrainedConstant({expression});
        TestMethodGeneric<{type}>({value});
        TestMethodGeneric<{type}>({expression});
        GenericClass<{type}>.TestMethodGeneric({value});
        GenericClass<{type}>.TestMethodGeneric({expression});
    }}
    public static void TestMethodWithConstant([ConstantExpected] {type} val) {{ }}
    public static void TestMethodWithConstrainedConstant([ConstantExpected(Min = {minValue}, Max = {maxValue})] {type} val) {{ }}
    public static void TestMethodGeneric<T>([ConstantExpected] T val) {{ }}
    
    public static class GenericClass<T>
    {{
        public static void TestMethodGeneric([ConstantExpected] T val) {{ }}
    }}
}}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("sbyte", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        [InlineData("short", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        [InlineData("int", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        [InlineData("long", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        [InlineData("byte", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        [InlineData("ushort", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        [InlineData("uint", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        [InlineData("ulong", "AEnum.One", "AEnum.Five", "AEnum.Two", "AEnum.One | AEnum.Two")]
        public static async Task TestEnumArgumentConstantAsync(string type, string minValue, string maxValue, string value, string expression)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod()
    {{
        TestMethodWithConstant({value});
        TestMethodWithConstant({expression});
        TestMethodWithConstrainedConstant({value});
        TestMethodWithConstrainedConstant({expression});
        TestMethodGeneric<AEnum>({value});
        TestMethodGeneric<AEnum>({expression});
        GenericClass<AEnum>.TestMethodGeneric({value});
        GenericClass<AEnum>.TestMethodGeneric({expression});
    }}
    public static void TestMethodWithConstant([ConstantExpected] AEnum val) {{ }}
    public static void TestMethodWithConstrainedConstant([ConstantExpected(Min = {minValue}, Max = {maxValue})] AEnum val) {{ }}
    public static void TestMethodGeneric<T>([ConstantExpected] T val) {{ }}
    
    public static class GenericClass<T>
    {{
        public static void TestMethodGeneric([ConstantExpected] T val) {{ }}
    }}
}}
 
public enum AEnum : {type}
{{
    Zero,
    One = 1,
    Two = 1 << 1,
    Three = 1 << 2,
    Four = 1 << 3,
    Five = 1 << 4
}}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("char")]
        [InlineData("sbyte")]
        [InlineData("short")]
        [InlineData("int")]
        [InlineData("long")]
        [InlineData("byte")]
        [InlineData("ushort")]
        [InlineData("uint")]
        [InlineData("ulong")]
        [InlineData("float")]
        [InlineData("double")]
        [InlineData("string")]
        public static async Task TestArgumentNotConstantAsync(string type)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod({type} nonConstant)
    {{
        TestMethodWithConstant({{|#0:nonConstant|}});
        TestMethodGeneric<{type}>({{|#1:nonConstant|}});
        GenenricClass<{type}>.TestMethodGeneric({{|#2:nonConstant|}});
    }}
    public static void TestMethodWithConstant([ConstantExpected] {type} val) {{ }}
    public static void TestMethodGeneric<T>([ConstantExpected] T val) {{ }}
    
    public static class GenenricClass<T>
    {{
        public static void TestMethodGeneric([ConstantExpected] T val) {{ }}
    }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantNotConstantRule)
                        .WithLocation(0),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantNotConstantRule)
                        .WithLocation(1),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantNotConstantRule)
                        .WithLocation(2));
        }
 
        [Theory]
        [InlineData("sbyte")]
        [InlineData("short")]
        [InlineData("int")]
        [InlineData("long")]
        [InlineData("byte")]
        [InlineData("ushort")]
        [InlineData("uint")]
        [InlineData("ulong")]
        public static async Task TestEnumArgumentNotConstantAsync(string type)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod(AEnum nonConstant)
    {{
        TestMethodWithConstant({{|#0:nonConstant|}});
        TestMethodGeneric<AEnum>({{|#1:nonConstant|}});
        GenenricClass<AEnum>.TestMethodGeneric({{|#2:nonConstant|}});
    }}
    public static void TestMethodWithConstant([ConstantExpected] AEnum val) {{ }}
    public static void TestMethodGeneric<T>([ConstantExpected] T val) {{ }}
    
    public static class GenenricClass<T>
    {{
        public static void TestMethodGeneric([ConstantExpected] T val) {{ }}
    }}
}}
 
public enum AEnum : {type}
{{
    One,
    Two
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantNotConstantRule)
                        .WithLocation(0),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantNotConstantRule)
                        .WithLocation(1),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantNotConstantRule)
                        .WithLocation(2));
        }
 
        [Theory]
        [InlineData("char", "(char)(object)10.5")]
        [InlineData("string", "(string)(object)20")]
        public static async Task TestArgumentInvalidConstantAsync(string type, string constant)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod()
    {{
        TestMethodWithConstant({{|#0:{constant}|}});
        TestMethodGeneric<{type}>({{|#1:{constant}|}});
        GenericClass<{type}>.TestMethodGeneric({{|#2:{constant}|}});
    }}
    public static void TestMethodWithConstant([ConstantExpected] {type} val) {{ }}
    public static void TestMethodGeneric<T>([ConstantExpected] T val) {{ }}
    
    public static class GenericClass<T>
    {{
        public static void TestMethodGeneric([ConstantExpected] T val) {{ }}
    }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantInvalidConstantRule)
                    .WithLocation(0)
                    .WithArguments(type),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantInvalidConstantRule)
                    .WithLocation(1)
                    .WithArguments(type),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantInvalidConstantRule)
                    .WithLocation(2)
                    .WithArguments(type));
        }
 
        [Theory]
        [InlineData("char", "'B'", "'C'", "'D'")]
        [InlineData("sbyte", "3", "4", "5")]
        [InlineData("short", "3", "4", "5")]
        [InlineData("int", "3", "4", "5")]
        [InlineData("long", "3", "4", "5")]
        [InlineData("byte", "3", "4", "5")]
        [InlineData("ushort", "3", "4", "5")]
        [InlineData("uint", "3", "4", "5")]
        [InlineData("ulong", "3", "4", "5")]
        [InlineData("float", "3", "4", "5")]
        [InlineData("double", "3", "4", "5")]
        public static async Task TestArgumentOutOfBoundsConstantAsync(string type, string min, string max, string testValue)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod()
    {{
        TestMethodWithConstant({{|#0:{testValue}|}});
    }}
    public static void TestMethodWithConstant([ConstantExpected(Min = {min}, Max = {max})] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantOutOfBoundsRule)
                        .WithLocation(0)
                        .WithArguments(min.Trim('\''), max.Trim('\'')));
        }
 
        [Theory]
        [InlineData("sbyte", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        [InlineData("short", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        [InlineData("int", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        [InlineData("long", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        [InlineData("byte", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        [InlineData("ushort", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        [InlineData("uint", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        [InlineData("ulong", "AEnum.Three", "AEnum.Four", "AEnum.Five")]
        public static async Task TestEnumArgumentOutOfBoundsConstantAsync(string type, string min, string max, string testValue)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod()
    {{
        TestMethodWithConstant({{|#0:{testValue}|}});
    }}
    public static void TestMethodWithConstant([ConstantExpected(Min = {min}, Max = {max})] AEnum val) {{ }}
}}
 
public enum AEnum : {type}
{{
    Zero,
    One,
    Two,
    Three,
    Four,
    Five
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantOutOfBoundsRule)
                        .WithLocation(0)
                        .WithArguments("3", "4"));
        }
 
        [Fact]
        public static async Task TestArgumentInvalidGenericTypeParameterConstantAsync()
        {
            string csInput = @"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{
    public static void TestMethod(int[] nonConstant)
    {
        TestMethodGeneric<int[]>(nonConstant); // ignore scenario
        GenericClass<int[]>.TestMethodGeneric(nonConstant); // ignore scenario
    }
    public static void TestMethodGeneric<T>([ConstantExpected] T val) { }
    
    public static class GenericClass<T>
    {
        public static void TestMethodGeneric([ConstantExpected] T val) { }
    }
}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("char", "'B'", "'C'")]
        [InlineData("sbyte", "3", "4")]
        [InlineData("short", "3", "4")]
        [InlineData("int", "3", "4")]
        [InlineData("long", "3", "4")]
        [InlineData("byte", "3", "4")]
        [InlineData("ushort", "3", "4")]
        [InlineData("uint", "3", "4")]
        [InlineData("ulong", "3", "4")]
        [InlineData("float", "3", "4")]
        [InlineData("double", "3", "4")]
        [InlineData("bool", "false", "false")]
        public static async Task TestConstantCompositionAsync(string type, string min, string max)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod([ConstantExpected] {type} constant)
    {{
        TestMethodWithConstant(constant);
    }}
    public static void TestMethodWithConstant([ConstantExpected] {type} val) {{ }}
    public static void TestMethodConstrained([ConstantExpected(Min = {min}, Max = {max})] {type} constant)
    {{
        TestMethodWithConstant(constant);
        TestMethodWithConstrainedConstant(constant);
    }}
    public static void TestMethodWithConstrainedConstant([ConstantExpected(Min = {min}, Max = {max})] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("sbyte", "AEnum.Two", "AEnum.Three")]
        [InlineData("short", "AEnum.Two", "AEnum.Three")]
        [InlineData("int", "AEnum.Two", "AEnum.Three")]
        [InlineData("long", "AEnum.Two", "AEnum.Three")]
        [InlineData("byte", "AEnum.Two", "AEnum.Three")]
        [InlineData("ushort", "AEnum.Two", "AEnum.Three")]
        [InlineData("uint", "AEnum.Two", "AEnum.Three")]
        [InlineData("ulong", "AEnum.Two", "AEnum.Three")]
        public static async Task TestEnumConstantCompositionAsync(string type, string min, string max)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethod([ConstantExpected] AEnum constant)
    {{
        TestMethodWithConstant(constant);
    }}
    public static void TestMethodWithConstant([ConstantExpected] AEnum val) {{ }}
    public static void TestMethodConstrained([ConstantExpected(Min = {min}, Max = {max})] AEnum constant)
    {{
        TestMethodWithConstant(constant);
        TestMethodWithConstrainedConstant(constant);
    }}
    public static void TestMethodWithConstrainedConstant([ConstantExpected(Min = {min}, Max = {max})] AEnum val) {{ }}
}}
 
public enum AEnum : {type}
{{
    Zero,
    One = 1,
    Two = 1 << 1,
    Three = 1 << 2,
    Four = 1 << 3,
    Five = 1 << 4
}}
";
            await TestCSAsync(csInput);
        }
 
        [Fact]
        public static async Task TestConstantCompositionStringAsync()
        {
            string csInput = @"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{
    public static void TestMethod([ConstantExpected] string constant)
    {
        TestMethodWithConstant(constant);
    }
    public static void TestMethodWithConstant([ConstantExpected] string val) { }
}
";
            await TestCSAsync(csInput);
        }
 
        [Theory]
        [InlineData("char", "'B'", "'C'", "'D'", 'B', 'C')]
        [InlineData("sbyte", "3", "4", "5", 3, 4)]
        [InlineData("short", "3", "4", "5", 3, 4)]
        [InlineData("int", "3", "4", "5", 3, 4)]
        [InlineData("long", "3", "4", "5", 3, 4)]
        [InlineData("byte", "3", "4", "5", 3, 4)]
        [InlineData("ushort", "3", "4", "5", 3, 4)]
        [InlineData("uint", "3", "4", "5", 3, 4)]
        [InlineData("ulong", "3", "4", "5", 3, 4)]
        [InlineData("float", "3", "4", "5", 3, 4)]
        [InlineData("double", "3", "4", "5", 3, 4)]
        [InlineData("bool", "false", "false", "true", false, false)]
        public static async Task TestConstantCompositionOutOfBoundsAsync(string type, string min, string max, string outOfBoundMax, object minValue, object maxValue)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethodConstrained([ConstantExpected(Min = {min}, Max = {outOfBoundMax})] {type} constant)
    {{
        TestMethodWithConstrainedConstant({{|#0:constant|}});
    }}
    public static void TestMethodWithConstrainedConstant([ConstantExpected(Min = {min}, Max = {max})] {type} val) {{ }}
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantOutOfBoundsRule)
                        .WithLocation(0)
                        .WithArguments(minValue.ToString(), maxValue.ToString()));
        }
 
        [Theory]
        [InlineData("sbyte", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        [InlineData("short", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        [InlineData("int", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        [InlineData("long", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        [InlineData("byte", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        [InlineData("ushort", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        [InlineData("uint", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        [InlineData("ulong", "AEnum.Two", "AEnum.Three", "AEnum.Four", 2, 4)]
        public static async Task TestEnumConstantCompositionOutOfBoundsAsync(string type, string min, string max, string outOfBoundMax, object minValue, object maxValue)
        {
            string csInput = @$"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{{
    public static void TestMethodConstrained([ConstantExpected(Min = {min}, Max = {outOfBoundMax})] AEnum constant)
    {{
        TestMethodWithConstrainedConstant({{|#0:constant|}});
    }}
    public static void TestMethodWithConstrainedConstant([ConstantExpected(Min = {min}, Max = {max})] AEnum val) {{ }}
}}
 
public enum AEnum : {type}
{{
    Zero,
    One = 1,
    Two = 1 << 1,
    Three = 1 << 2,
    Four = 1 << 3,
    Five = 1 << 4
}}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantOutOfBoundsRule)
                        .WithLocation(0)
                        .WithArguments(minValue.ToString(), maxValue.ToString()));
        }
 
        [Fact]
        public static async Task TestConstantCompositionNotSameTypeAsync()
        {
            string csInput = @"
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
 
public class Test
{
    public static void TestMethod([ConstantExpected] long constant)
    {
        TestMethodWithConstant({|#0:(int)constant|});
        TestMethodWithStringConstant({|#1:(string)(object)constant|});
    }
    public static void TestMethod([ConstantExpected] short constant)
    {
        TestMethodWithConstant({|#2:constant|});
    }
    public static void TestMethodWithConstant([ConstantExpected] int val) { }
    public static void TestMethodWithStringConstant([ConstantExpected] string val) { }
}
";
            await TestCSAsync(csInput,
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantInvalidConstantRule)
                        .WithLocation(0)
                        .WithArguments("int"),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantInvalidConstantRule)
                    .WithLocation(1)
                    .WithArguments("string"),
                VerifyCS.Diagnostic(ConstantExpectedAnalyzer.CA1857.ConstantInvalidConstantRule)
                        .WithLocation(2)
                        .WithArguments("int"));
        }
 
        private static async Task TestCSAsync(string source, params DiagnosticResult[] diagnosticResults)
        {
            var test = new VerifyCS.Test
            {
                TestCode = source,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
                LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.Preview,
            };
            test.ExpectedDiagnostics.AddRange(diagnosticResults);
            await test.RunAsync();
        }
 
        private static async Task TestCSMissingAttributeAsync(string source, params DiagnosticResult[] diagnosticResults)
        {
            var test = new VerifyCS.Test
            {
                TestCode = source,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.Preview,
            };
            test.TestState.Sources.Add(SimilarAttributeSource);
            test.ExpectedDiagnostics.AddRange(diagnosticResults);
            await test.RunAsync();
        }
 
        private const string SimilarAttributeSource = @"#nullable enable
using System;
namespace Similar
{
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public sealed class ConstantExpectedAttribute : Attribute
    {
        public object? Min { get; set; }
        public object? Max { get; set; }
    }
}";
    }
}