// 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.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Shared.Extensions;
using Microsoft.CodeAnalysis.CSharp.UseUtf8StringLiteral;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUtf8StringLiteral
{
    using VerifyCS = CSharpCodeFixVerifier<
        UseUtf8StringLiteralDiagnosticAnalyzer,
        UseUtf8StringLiteralCodeFixProvider>;

    [Trait(Traits.Feature, Traits.Features.CodeActionsUseUtf8StringLiteral)]
    public class UseUtf8StringLiteralTests
    {
        [Fact]
        public async Task TestNotInAttribute()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class MyAttribute : System.Attribute
                {
                    public MyAttribute(byte[] data)
                    {
                    }
                }

                public class C
                {
                    [MyAttribute(new byte[] { 65, 66, 67 })]
                    public void M()
                    {
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotInCSharp10()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[] { 65, 66, 67 };
                    }
                }
                """,
                LanguageVersion = LanguageVersion.CSharp10
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWhenNoReadOnlySpan()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[] { 65, 66, 67 };
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net20.Default,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWithoutInitializer()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[10];
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotInExpressionTree()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                using System;
                using System.Linq.Expressions;

                public class C
                {
                    public void M()
                    {
                        N(() => new byte[] { 65, 66, 67 });
                    }

                    public void N(Expression<Func<byte[]>> f)
                    {
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWhenNotByteArray()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new int[] { 65, 66, 67 };
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWhenOptionNotSet()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[] { 65, 66, 67 };
                    }
                }
                """,
                EditorConfig = """
                [*.cs]
                csharp_style_prefer_utf8_string_literals = false
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWhenNonLiteralElement()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[] { 65, GetB(), 67 };
                    }

                    public byte GetB() => 66;
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestNotWhenMultidimensionalArray()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[,] { { 65, 66 }, { 67, 68 }, { 69, 70 } };
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestSimpleByteArray()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 65, 66, 67 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "ABC"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestConstant()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    private const byte B = 66;
                    public void M()
                    {
                        var x = [|new|] byte[] { 65, B, 67 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    private const byte B = 66;
                    public void M()
                    {
                        var x = "ABC"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestImplicitArray()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] [] { (byte)65, (byte)66, (byte)67 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "ABC"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestExplicitCast()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 65, (byte)'B', 67 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "ABC"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestHexLiteral()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 0x41, 0x42, 0x43 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "ABC"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestBinaryExpression()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 60 + 5, 60 + 6, 60 + 7 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "ABC"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestEmptyArray()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = ""u8.ToArray();
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestTrivia1()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 65, 66, 67 }; // I wish this byte array was easier to read
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "ABC"u8.ToArray(); // I wish this byte array was easier to read
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestTrivia2()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(byte[] b)
                    {
                        M(/* arrays are */ [|new|] byte[] { 65, 66, 67 } /* cool */);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(byte[] b)
                    {
                        M(/* arrays are */ "ABC"u8.ToArray() /* cool */);
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestMultiple()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 0x41, 0x42, 0x43 };
                        var y = [|new|] byte[] { 0x44, 0x45, 0x46 };
                        var z = [|new|] byte[] { 0x47, 0x48, 0x49 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "ABC"u8.ToArray();
                        var y = "DEF"u8.ToArray();
                        var z = "GHI"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestEscapeChars()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 34, 92, 10, 13, 9 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "\"\\\n\r\t"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestEmoji()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 240, 159, 152, 128 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "😀"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestHalfEmoji1()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[] { 240, 159 };
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestHalfEmoji2()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[] { 152, 128 };
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestHalfEmoji3()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = new byte[] { 65, 152, 128, 66 };
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestUnicodeReplacementChar()
        {
            // The unicode replacement character is what is returned when, for example, an unpaired
            // surrogate is converted to a UTF-8 string. This test just ensures that the presence of
            // that character isn't being used to detect a failure state of some kind.
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = [|new|] byte[] { 239, 191, 189 };
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M()
                    {
                        var x = "�"u8.ToArray();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestCollectionInitializer()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                using System.Collections;
                using System.Collections.Generic;

                class C : IEnumerable<int>
                {
                    void M(C c)
                    {
                        // Each literal of the three is a separate IArrayCreationOperation
                        // Lowered code is similar to:
                        /*
                            C c = new C();
                            c.Add(new byte[] { 65 });
                            c.Add(new byte[] { 66 });
                            c.Add(new byte[] { 67 });
                        */
                        c = new() { [|65|], [|66|], [|67|] };
                    }

                    public void Add(params byte[] bytes)
                    {
                    }

                    public IEnumerator<int> GetEnumerator()
                    {
                        throw new System.NotImplementedException();
                    }

                    IEnumerator IEnumerable.GetEnumerator()
                    {
                        throw new System.NotImplementedException();
                    }
                }
                """,
                FixedCode =
                """
                using System.Collections;
                using System.Collections.Generic;

                class C : IEnumerable<int>
                {
                    void M(C c)
                    {
                        // Each literal of the three is a separate IArrayCreationOperation
                        // Lowered code is similar to:
                        /*
                            C c = new C();
                            c.Add(new byte[] { 65 });
                            c.Add(new byte[] { 66 });
                            c.Add(new byte[] { 67 });
                        */
                        c = new() { "A"u8.ToArray(), "B"u8.ToArray(), "C"u8.ToArray() };
                    }

                    public void Add(params byte[] bytes)
                    {
                    }

                    public IEnumerator<int> GetEnumerator()
                    {
                        throw new System.NotImplementedException();
                    }

                    IEnumerator IEnumerable.GetEnumerator()
                    {
                        throw new System.NotImplementedException();
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestUsingWithParamArray()
        {
            // From: https://github.com/dotnet/roslyn/blob/0c7c0b33f0871fc4308eb2d75d77b87fc9293290/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IUsingStatement.cs#L1189-L1194
            // There is an array creation operation for the param array
            await new VerifyCS.Test
            {
                TestCode =
                """
                class C
                {
                    public static void M1()
                    {
                        using(var s = new S())
                        { 
                        }
                    }
                }
                ref struct S
                {
                    public void Dispose(int a = 1, bool b = true, params byte[] others) { }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Theory]
        // Standard C# escape characters
        [InlineData(new byte[] { 0, 7, 8, 12, 11 })]
        // Various cases copied from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/HuffmanDecodingTests.cs
        [InlineData(new byte[] { 0xff, 0xcf })]
        [InlineData(new byte[] { 0b100111_00, 0b101_10100, 0b0_101000_0, 0b0111_1111 })]
        [InlineData(new byte[] { 0xb6, 0xb9, 0xac, 0x1c, 0x85, 0x58, 0xd5, 0x20, 0xa4, 0xb6, 0xc2, 0xad, 0x61, 0x7b, 0x5a, 0x54, 0x25, 0x1f })]
        [InlineData(new byte[] { 0xfe, 0x53 })]
        [InlineData(new byte[] { 0xff, 0xff, 0xf6, 0xff, 0xff, 0xfd, 0x68 })]
        [InlineData(new byte[] { 0xff, 0xff, 0xf9, 0xff, 0xff, 0xfd, 0x86 })]
        // Various cases copied from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs
        [InlineData(new byte[] { 0xa8, 0xbe, 0x16, 0x9c, 0xa3, 0x90, 0xb6, 0x7f })]
        [InlineData(new byte[] { 0x37, 0x02, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65 })]
        [InlineData(new byte[] { 0x3f, 0x01 })]
        // DaysInMonth365 from https://github.com/dotnet/runtime/blob/b5a8ece073110140e2d9696cdfdc047ec78c2fa1/src/libraries/System.Private.CoreLib/src/System/DateTime.cs
        [InlineData(new byte[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 })]
        public async Task TestInvalidUtf8Strings(byte[] bytes)
        {
            await new VerifyCS.Test
            {
                TestCode =
$@"
public class C
{{
    private static readonly byte[] _bytes = new byte[] {{ {string.Join(", ", bytes)} }};
}}
",
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestDoesNotOfferForControlCharacters()
        {
            // Copied from https://github.com/dotnet/runtime/blob/6a889d234267a4c96ed21d0e1660dce787d78a38/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Conversion.cs
            var input = """
                class C
                {
                    internal enum ConvKind
                    {
                        Identity = 1,  // Identity conversion
                        Implicit = 2,  // Implicit conversion
                        Explicit = 3,  // Explicit conversion
                        Unknown = 4,  // Unknown so call canConvert
                        None = 5,  // None
                    }

                    private const byte ID = (byte)ConvKind.Identity;  // 0x01
                    private const byte IMP = (byte)ConvKind.Implicit; // 0x02
                    private const byte EXP = (byte)ConvKind.Explicit; // 0x03
                    private const byte NO = (byte)ConvKind.None;      // 0x05
                    private const byte CONV_KIND_MASK = 0x0F;
                    private const byte UDC = 0x40;
                    private const byte XUD = EXP | UDC;
                    private const byte IUD = IMP | UDC;

                    private static readonly byte[][] s_simpleTypeConversions =
                    {
                        // to:                   BYTE I2   I4   I8   FLT  DBL  DEC  CHAR BOOL SBYTE U2   U4   U8
                        /* from */
                         new byte[] /* BYTE */ { ID,  IMP, IMP, IMP, IMP, IMP, IUD, EXP, NO,  EXP,  IMP, IMP, IMP },
                         new byte[] /*   I2 */ { EXP, ID,  IMP, IMP, IMP, IMP, IUD, EXP, NO,  EXP,  EXP, EXP, EXP },
                         new byte[] /*   I4 */ { EXP, EXP, ID,  IMP, IMP, IMP, IUD, EXP, NO,  EXP,  EXP, EXP, EXP },
                         new byte[] /*   I8 */ { EXP, EXP, EXP, ID,  IMP, IMP, IUD, EXP, NO,  EXP,  EXP, EXP, EXP },
                         new byte[] /*  FLT */ { EXP, EXP, EXP, EXP, ID,  IMP, XUD, EXP, NO,  EXP,  EXP, EXP, EXP },
                         new byte[] /*  DBL */ { EXP, EXP, EXP, EXP, EXP, ID,  XUD, EXP, NO,  EXP,  EXP, EXP, EXP },
                         new byte[] /*  DEC */ { XUD, XUD, XUD, XUD, XUD, XUD, ID,  XUD, NO,  XUD,  XUD, XUD, XUD },
                         new byte[] /* CHAR */ { EXP, EXP, IMP, IMP, IMP, IMP, IUD, ID,  NO,  EXP,  IMP, IMP, IMP },
                         new byte[] /* BOOL */ { NO,  NO,  NO,  NO,  NO,  NO,  NO,  NO,  ID,  NO,   NO,  NO,  NO  },
                         new byte[] /*SBYTE */ { EXP, IMP, IMP, IMP, IMP, IMP, IUD, EXP, NO,  ID,   EXP, EXP, EXP },
                         new byte[] /*   U2 */ { EXP, EXP, IMP, IMP, IMP, IMP, IUD, EXP, NO,  EXP,  ID,  IMP, IMP },
                         new byte[] /*   U4 */ { EXP, EXP, EXP, IMP, IMP, IMP, IUD, EXP, NO,  EXP,  EXP, ID,  IMP },
                         new byte[] /*   U8 */ { EXP, EXP, EXP, EXP, IMP, IMP, IUD, EXP, NO,  EXP,  EXP, EXP, ID  },
                    };
                }
                """;

            await new VerifyCS.Test
            {
                TestCode = input,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray1()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M([|new|] byte[] { 65, 66, 67 });
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M("ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray2()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int i, params byte[] b)
                    {
                        M(1, [|new|] byte[] { 65, 66, 67 });
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(int i, params byte[] b)
                    {
                        M(1, "ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray3()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M([|65|]);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M("A"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray4()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M(/* hi */ [|65|] /* there */);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M(/* hi */ "A"u8.ToArray() /* there */);
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray5()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M([|65, 66, 67|]);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M("ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray6()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M(/* hi */ [|65, 66, 67|] /* there */);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(params byte[] b)
                    {
                        M(/* hi */ "ABC"u8.ToArray() /* there */);
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray7()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int x, params byte[] b)
                    {
                        M(1);
                    }
                }
                """,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray8()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int x, params byte[] b)
                    {
                        M(1, [|65, 66, 67|]);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(int x, params byte[] b)
                    {
                        M(1, "ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray9()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int x, params byte[] b)
                    {
                        M(1, /* hi */ [|65|] /* there */);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(int x, params byte[] b)
                    {
                        M(1, /* hi */ "A"u8.ToArray() /* there */);
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray10()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int x, params byte[] b)
                    {
                        M(1, /* hi */ [|65, 66, 67|] /* there */);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(int x, params byte[] b)
                    {
                        M(1, /* hi */ "ABC"u8.ToArray() /* there */);
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray11()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int x, int y, int z, params byte[] b)
                    {
                        M( /* b1 */ 1 /* a1 */, /* b2 */ 2 /* a2 */, /* b3 */ 3 /* a3 */, /* b4 */ [|65, /* x1 */ 66, /* x2 */  67|] /* a4 */);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(int x, int y, int z, params byte[] b)
                    {
                        M( /* b1 */ 1 /* a1 */, /* b2 */ 2 /* a2 */, /* b3 */ 3 /* a3 */, /* b4 */ "ABC"u8.ToArray() /* a4 */);
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray12()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public C(params byte[] b)
                    {
                        new C([|65, 66, 67|]);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public C(params byte[] b)
                    {
                        new C("ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray13()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public int this[params byte[] bytes]
                    {
                        get => 0;
                    }

                    public void M()
                    {
                        _ = this[[|65, 66, 67|]];
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public int this[params byte[] bytes]
                    {
                        get => 0;
                    }

                    public void M()
                    {
                        _ = this["ABC"u8.ToArray()];
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray14()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public record C1(int x) : B([|65, 66, 67|]);

                public record C2(params byte[] Bytes) : B(Bytes);

                public record B(params byte[] Bytes)
                {
                    public void M()
                    {
                        new C1(1);
                        new C2([|65, 66, 67|]);
                        new B([|65, 66, 67|]);
                    }
                }
                namespace System.Runtime.CompilerServices
                {
                    public sealed class IsExternalInit
                    {
                    }
                }
                """,
                FixedCode =
                """
                public record C1(int x) : B("ABC"u8.ToArray());

                public record C2(params byte[] Bytes) : B(Bytes);

                public record B(params byte[] Bytes)
                {
                    public void M()
                    {
                        new C1(1);
                        new C2("ABC"u8.ToArray());
                        new B("ABC"u8.ToArray());
                    }
                }
                namespace System.Runtime.CompilerServices
                {
                    public sealed class IsExternalInit
                    {
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray15()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C1 : B
                {
                    public C1(int x)
                        : base([|65, 66, 67|])
                    {
                    }
                }

                public class C2 : B
                {
                    public C2(params byte[] Bytes)
                        : base(Bytes)
                    {
                    }
                }

                public class B
                {
                    public B(string x, params byte[] bytes)
                        : this(bytes)
                    {
                    }

                    public B(int x)
                        : this([|65, 66, 67|])
                    {
                    }

                    public B(params byte[] bytes)
                    {
                        new C1(1);
                        new C2([|65, 66, 67|]);
                        new B([|65, 66, 67|]);
                        new B("a", [|65, 66, 67|]);
                    }
                }
                """,
                FixedCode =
                """
                public class C1 : B
                {
                    public C1(int x)
                        : base("ABC"u8.ToArray())
                    {
                    }
                }

                public class C2 : B
                {
                    public C2(params byte[] Bytes)
                        : base(Bytes)
                    {
                    }
                }

                public class B
                {
                    public B(string x, params byte[] bytes)
                        : this(bytes)
                    {
                    }

                    public B(int x)
                        : this("ABC"u8.ToArray())
                    {
                    }

                    public B(params byte[] bytes)
                    {
                        new C1(1);
                        new C2("ABC"u8.ToArray());
                        new B("ABC"u8.ToArray());
                        new B("a", "ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray16()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int[] i, byte[] b)
                    {
                        M(new int[] { 1 }, [|new|] byte[] { 65, 66, 67 });
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(int[] i, byte[] b)
                    {
                        M(new int[] { 1 }, "ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestParamArray17()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(int[] i, params byte[] b)
                    {
                        M(new int[] { 1 }, [|65, 66, 67|]);
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(int[] i, params byte[] b)
                    {
                        M(new int[] { 1 }, "ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestMultidimensionalArray()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                public class C
                {
                    public void M(byte[][] i, byte[] b)
                    {
                        M(new byte[][] { [|new|] byte[] { 65, 66, 67 }, [|new|] byte[] { 65, 66, 67 } }, [|new|] byte[] { 65, 66, 67 });
                    }
                }
                """,
                FixedCode =
                """
                public class C
                {
                    public void M(byte[][] i, byte[] b)
                    {
                        M(new byte[][] { "ABC"u8.ToArray(), "ABC"u8.ToArray() }, "ABC"u8.ToArray());
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestTargettingReadOnlySpan1()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                using System;

                public class C
                {
                    public void M()
                    {
                        ReadOnlySpan<byte> x = [|new|] byte[] { 65, 66, 67 };
                    }
                }
                """,
                FixedCode =
                """
                using System;

                public class C
                {
                    public void M()
                    {
                        ReadOnlySpan<byte> x = "ABC"u8;
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }

        [Fact]
        public async Task TestTargettingReadOnlySpan2()
        {
            await new VerifyCS.Test
            {
                TestCode =
                """
                using System;

                public class C
                {
                    public void M(ReadOnlySpan<byte> x)
                    {
                        M(/* 1 */[|new|] byte[] { 65, 66, 67 }/* 2 */);
                    }
                }
                """,
                FixedCode =
                """
                using System;

                public class C
                {
                    public void M(ReadOnlySpan<byte> x)
                    {
                        M(/* 1 */"ABC"u8/* 2 */);
                    }
                }
                """,
                CodeActionValidationMode = CodeActionValidationMode.None,
                ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
                LanguageVersion = LanguageVersion.CSharp12
            }.RunAsync();
        }
    }
}
