﻿// 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.

#nullable disable

using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.ConvertForEachToFor;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertForEachToFor
{
    [Trait(Traits.Feature, Traits.Features.CodeActionsConvertForEachToFor)]
    public partial class ConvertForEachToForTests : AbstractCSharpCodeActionTest
    {
        protected override CodeRefactoringProvider CreateCodeRefactoringProvider(
            Workspace workspace, TestParameters parameters)
            => new CSharpConvertForEachToForCodeRefactoringProvider();

        private readonly CodeStyleOption2<bool> onWithSilent = new CodeStyleOption2<bool>(true, NotificationOption2.Silent);

        private OptionsCollection ImplicitTypeEverywhere
            => new(GetLanguage())
            {
                { CSharpCodeStyleOptions.VarElsewhere, onWithSilent },
                { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithSilent },
                { CSharpCodeStyleOptions.VarForBuiltInTypes, onWithSilent },
            };

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task EmptyBlockBody()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach[||](var a in array)
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task EmptyBody()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach[||](var a in array) ;
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++) ;
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task Body()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach[||](var a in array) Console.WriteLine(a);
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task BlockBody()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach[||](var a in array)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task Comment()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        /* comment */
                        foreach[||](var a in array) /* comment */
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        /* comment */
                        for (int {|Rename:i|} = 0; i < array.Length; i++) /* comment */
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task Comment2()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach[||](var a in array)
                        /* comment */
                        {
                        }/* comment */
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        /* comment */
                        {
                            int a = array[i];
                        }/* comment */
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task Comment3()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach[||](var a in array) /* comment */;
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++) /* comment */;
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task Comment4()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach[||](var a in array) Console.WriteLine(a); // test
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                            Console.WriteLine(a); // test
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task Comment5()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (var a in array) /* test */ Console.WriteLine(a); 
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++) /* test */
                        {
                            int a = array[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task Comment6()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (var a in array) 
                            /* test */ Console.WriteLine(a); 
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                            /* test */
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task Comment7()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        // test
                        foreach[||](var a in new int[] { 1, 3, 4 })
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        // test
                        int[] {|Rename:array|} = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task TestCommentsInTheMiddleOfParentheses()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (var a /* test */ in array) ;
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++) ;
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task TestCommentsAtBeginningOfParentheses()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (/* test */ var a in array) ;
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++) ;
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task TestCommentsAtTheEndOfParentheses()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (var a in array /* test */) ;
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++) ;
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task CollectionStatement()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        foreach[||](var a in new int[] { 1, 3, 4 })
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        int[] {|Rename:array|} = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task CollectionConflict()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = 1;

                        foreach[||](var a in new int[] { 1, 3, 4 })
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = 1;

                        int[] {|Rename:array1|} = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array1.Length; i++)
                        {
                            int a = array1[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task VariableWritten()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new[] { 1 };
                        foreach [||] (var a in array)
                        {
                            a = 1;
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new[] { 1 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            {|Warning:int a = array[i];|}
                            a = 1;
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task IndexConflict()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (var a in array)
                        {
                            int i = 0;
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i1|} = 0; i1 < array.Length; i1++)
                        {
                            int a = array[i1];
                            int i = 0;
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task StructPropertyReadFromAndDiscarded()
        {
            var text = """
                class Test
                {
                    struct Struct
                    {
                        public string Property { get; }
                    }

                    void Method()
                    {
                        var array = new[] { new Struct() };
                        foreach [||] (var a in array)
                        {
                            _ = a.Property;
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    struct Struct
                    {
                        public string Property { get; }
                    }

                    void Method()
                    {
                        var array = new[] { new Struct() };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            Struct a = array[i];
                            _ = a.Property;
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task StructPropertyReadFromAndAssignedToLocal()
        {
            var text = """
                class Test
                {
                    struct Struct
                    {
                        public string Property { get; }
                    }

                    void Method()
                    {
                        var array = new[] { new Struct() };
                        foreach [||] (var a in array)
                        {
                            var b = a.Property;
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    struct Struct
                    {
                        public string Property { get; }
                    }

                    void Method()
                    {
                        var array = new[] { new Struct() };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            Struct a = array[i];
                            var b = a.Property;
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task WrongCaretPosition()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach (var a in array)
                        {
                            [||] 
                        }
                    }
                }
                """;
            await TestMissingInRegularAndScriptAsync(text);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task TestCaretBefore()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        [||] foreach(var a in array)
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task TestCaretAfter()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach(var a in array) [||]
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++) 
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task TestSelection()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        [|foreach(var a in array)
                        {
                        }|]
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task Field()
        {
            var text = """
                class Test
                {
                    int[] array = new int[] { 1, 3, 4 };

                    void Method()
                    {
                        foreach [||] (var a in array)
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    int[] array = new int[] { 1, 3, 4 };

                    void Method()
                    {
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task ArrayElement()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[][] { new int[] { 1, 3, 4 } };
                        foreach [||] (var a in array[0])
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[][] { new int[] { 1, 3, 4 } };
                        for (int {|Rename:i|} = 0; i < array[0].Length; i++)
                        {
                            int a = array[0][i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task Parameter()
        {
            var text = """
                class Test
                {
                    void Method(int[] array)
                    {
                        foreach [||] (var a in array)
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method(int[] array)
                    {
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31621")]
        public async Task Property()
        {
            var text = """
                class Test
                {
                    int [] Prop { get; } = new int[] { 1, 2, 3 };

                    void Method()
                    {
                        foreach [||] (var a in Prop)
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    int [] Prop { get; } = new int[] { 1, 2, 3 };

                    void Method()
                    {
                        for (int {|Rename:i|} = 0; i < Prop.Length; i++)
                        {
                            int a = Prop[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task Interface()
        {
            var text = """
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var array = (IList<int>)(new int[] { 1, 3, 4 });
                        foreach[||] (var a in array)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var array = (IList<int>)(new int[] { 1, 3, 4 });
                        for (int {|Rename:i|} = 0; i < array.Count; i++)
                        {
                            int a = array[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task IListOfT()
        {
            var text = """
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new List<int>();
                        foreach [||](var a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new List<int>();
                        for (int {|Rename:i|} = 0; i < list.Count; i++)
                        {
                            int a = list[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task IReadOnlyListOfT()
        {
            var text = """
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new ReadOnly<int>();
                        foreach [||](var a in list)
                        {
                            Console.WriteLine(a);
                        }

                    }
                }

                class ReadOnly<T> : IReadOnlyList<T>
                {
                    public T this[int index] => throw new System.NotImplementedException();
                    public int Count => throw new System.NotImplementedException();
                    public IEnumerator<T> GetEnumerator() => throw new System.NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException();
                }
                """;
            var expected = """
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new ReadOnly<int>();
                        for (int {|Rename:i|} = 0; i < list.Count; i++)
                        {
                            int a = list[i];
                            Console.WriteLine(a);
                        }

                    }
                }

                class ReadOnly<T> : IReadOnlyList<T>
                {
                    public T this[int index] => throw new System.NotImplementedException();
                    public int Count => throw new System.NotImplementedException();
                    public IEnumerator<T> GetEnumerator() => throw new System.NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException();
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task IList()
        {
            var text = """
                using System;
                using System.Collections;

                class Test
                {
                    void Method()
                    {
                        var list = new List();
                        foreach [||](var a in list)
                        {
                            Console.WriteLine(a);
                        }

                    }
                }

                class List : IList
                {
                    public object this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
                    public bool IsReadOnly => throw new NotImplementedException();
                    public bool IsFixedSize => throw new NotImplementedException();
                    public int Count => throw new NotImplementedException();
                    public object SyncRoot => throw new NotImplementedException();
                    public bool IsSynchronized => throw new NotImplementedException();
                    public int Add(object value) => throw new NotImplementedException();
                    public void Clear() => throw new NotImplementedException();
                    public bool Contains(object value) => throw new NotImplementedException();
                    public void CopyTo(Array array, int index) => throw new NotImplementedException();
                    public IEnumerator GetEnumerator() => throw new NotImplementedException();
                    public int IndexOf(object value) => throw new NotImplementedException();
                    public void Insert(int index, object value) => throw new NotImplementedException();
                    public void Remove(object value) => throw new NotImplementedException();
                    public void RemoveAt(int index) => throw new NotImplementedException();
                }
                """;
            var expected = """
                using System;
                using System.Collections;

                class Test
                {
                    void Method()
                    {
                        var list = new List();
                        for (int {|Rename:i|} = 0; i < list.Count; i++)
                        {
                            object a = list[i];
                            Console.WriteLine(a);
                        }

                    }
                }

                class List : IList
                {
                    public object this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
                    public bool IsReadOnly => throw new NotImplementedException();
                    public bool IsFixedSize => throw new NotImplementedException();
                    public int Count => throw new NotImplementedException();
                    public object SyncRoot => throw new NotImplementedException();
                    public bool IsSynchronized => throw new NotImplementedException();
                    public int Add(object value) => throw new NotImplementedException();
                    public void Clear() => throw new NotImplementedException();
                    public bool Contains(object value) => throw new NotImplementedException();
                    public void CopyTo(Array array, int index) => throw new NotImplementedException();
                    public IEnumerator GetEnumerator() => throw new NotImplementedException();
                    public int IndexOf(object value) => throw new NotImplementedException();
                    public void Insert(int index, object value) => throw new NotImplementedException();
                    public void Remove(object value) => throw new NotImplementedException();
                    public void RemoveAt(int index) => throw new NotImplementedException();
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/29740")]
        public async Task ImmutableArray()
        {
            var text = """
                <Workspace>
                    <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <MetadataReference>
                """ + typeof(ImmutableArray<>).Assembly.Location + """
                </MetadataReference>
                        <Document>
                using System;
                using System.Collections.Immutable;

                class Test
                {
                    void Method()
                    {
                        var list = ImmutableArray.Create(1);
                        foreach [||](var a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }</Document>
                    </Project>
                </Workspace>
                """;

            var expected = """
                using System;
                using System.Collections.Immutable;

                class Test
                {
                    void Method()
                    {
                        var list = ImmutableArray.Create(1);
                        for (int {|Rename:i|} = 0; i < list.Length; i++)
                        {
                            int a = list[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task ExplicitInterface()
        {
            var text = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Explicit();
                        foreach [||] (var a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }

                class Explicit : IReadOnlyList<int>
                {
                    int IReadOnlyList<int>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<int>.Count => throw new NotImplementedException();
                    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
                }
                """;

            var expected = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Explicit();
                        IReadOnlyList<int> {|Rename:list1|} = list;
                        for (int {|Rename:i|} = 0; i < list1.Count; i++)
                        {
                            int a = list1[i];
                            Console.WriteLine(a);
                        }
                    }
                }

                class Explicit : IReadOnlyList<int>
                {
                    int IReadOnlyList<int>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<int>.Count => throw new NotImplementedException();
                    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task DoubleExplicitInterface()
        {
            var text = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Explicit();
                        foreach [||] (var a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }

                class Explicit : IReadOnlyList<int>, IReadOnlyList<string>
                {
                    int IReadOnlyList<int>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<int>.Count => throw new NotImplementedException();
                    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                    string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                }
                """;
            await TestMissingInRegularAndScriptAsync(text);
        }

        [Fact]
        public async Task DoubleExplicitInterfaceWithExplicitType()
        {
            var text = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Explicit();
                        foreach [||] (int a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }

                class Explicit : IReadOnlyList<int>, IReadOnlyList<string>
                {
                    int IReadOnlyList<int>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<int>.Count => throw new NotImplementedException();
                    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                    string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                }
                """;

            var expected = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Explicit();
                        IReadOnlyList<int> {|Rename:list1|} = list;
                        for (int {|Rename:i|} = 0; i < list1.Count; i++)
                        {
                            int a = list1[i];
                            Console.WriteLine(a);
                        }
                    }
                }

                class Explicit : IReadOnlyList<int>, IReadOnlyList<string>
                {
                    int IReadOnlyList<int>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<int>.Count => throw new NotImplementedException();
                    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                    string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task MixedInterfaceImplementation()
        {
            var text = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Mixed();
                        foreach [||] (var a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }

                class Mixed : IReadOnlyList<int>, IReadOnlyList<string>
                {
                    public int this[int index] => throw new NotImplementedException();
                    public int Count => throw new NotImplementedException();
                    public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                    string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                }
                """;

            var expected = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Mixed();
                        for (int {|Rename:i|} = 0; i < list.Count; i++)
                        {
                            int a = list[i];
                            Console.WriteLine(a);
                        }
                    }
                }

                class Mixed : IReadOnlyList<int>, IReadOnlyList<string>
                {
                    public int this[int index] => throw new NotImplementedException();
                    public int Count => throw new NotImplementedException();
                    public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                    string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task MixedInterfaceImplementationWithExplicitType()
        {
            var text = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Mixed();
                        foreach [||] (string a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }

                class Mixed : IReadOnlyList<int>, IReadOnlyList<string>
                {
                    public int this[int index] => throw new NotImplementedException();
                    public int Count => throw new NotImplementedException();
                    public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                    string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                }
                """;

            var expected = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Mixed();
                        IReadOnlyList<string> {|Rename:list1|} = list;
                        for (int {|Rename:i|} = 0; i < list1.Count; i++)
                        {
                            string a = list1[i];
                            Console.WriteLine(a);
                        }
                    }
                }

                class Mixed : IReadOnlyList<int>, IReadOnlyList<string>
                {
                    public int this[int index] => throw new NotImplementedException();
                    public int Count => throw new NotImplementedException();
                    public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                    string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task PreserveUserExpression()
        {
            var text = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                namespace NS
                {
                    class Test
                    {
                        void Method()
                        {
                            foreach [||] (string a in new NS.Mixed())
                            {
                                Console.WriteLine(a);
                            }
                        }
                    }

                    class Mixed : IReadOnlyList<int>, IReadOnlyList<string>
                    {
                        public int this[int index] => throw new NotImplementedException();
                        public int Count => throw new NotImplementedException();
                        public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
                        IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                        string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                        int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                        IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                    }
                }
                """;

            var expected = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                namespace NS
                {
                    class Test
                    {
                        void Method()
                        {
                            IReadOnlyList<string> {|Rename:list|} = new NS.Mixed();
                            for (int {|Rename:i|} = 0; i < list.Count; i++)
                            {
                                string a = list[i];
                                Console.WriteLine(a);
                            }
                        }
                    }

                    class Mixed : IReadOnlyList<int>, IReadOnlyList<string>
                    {
                        public int this[int index] => throw new NotImplementedException();
                        public int Count => throw new NotImplementedException();
                        public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
                        IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();

                        string IReadOnlyList<string>.this[int index] => throw new NotImplementedException();
                        int IReadOnlyCollection<string>.Count => throw new NotImplementedException();
                        IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw new NotImplementedException();
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task EmbededStatement()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        if (true)
                            foreach [||] (var a in new int[] {});
                    }
                }
                """;
            await TestMissingInRegularAndScriptAsync(text);
        }

        [Fact]
        public async Task EmbededStatement2()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        if (true)
                            foreach [||] (var a in array) Console.WriteLine(a);
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        if (true)
                            for (int {|Rename:i|} = 0; i < array.Length; i++)
                            {
                                int a = array[i];
                                Console.WriteLine(a);
                            }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task IndexConflict2()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (var i in array)
                        {
                            Console.WriteLine(i);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i1|} = 0; i1 < array.Length; i1++)
                        {
                            int i = array[i1];
                            Console.WriteLine(i);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task UseTypeAsUsedInForeach()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        foreach [||] (int a in array)
                        {
                            Console.WriteLine(i);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 3, 4 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            int a = array[i];
                            Console.WriteLine(i);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task String()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        foreach [||] (var a in "test")
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        string {|Rename:str|} = "test";
                        for (int {|Rename:i|} = 0; i < str.Length; i++)
                        {
                            char a = str[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;

            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task StringLocalConst()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        const string test = "test";
                        foreach [||] (var a in test)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        const string test = "test";
                        for (int {|Rename:i|} = 0; i < test.Length; i++)
                        {
                            char a = test[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task StringConst()
        {
            var text = """
                class Test
                {
                    const string test = "test";

                    void Method()
                    {
                        foreach [||] (var a in test)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    const string test = "test";

                    void Method()
                    {
                        for (int {|Rename:i|} = 0; i < test.Length; i++)
                        {
                            char a = test[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task ElementExplicitCast()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new object[] { 1, 2, 3 };
                        foreach [||] (string a in array)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var array = new object[] { 1, 2, 3 };
                        for (int {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            string a = (string)array[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50469")]
        public async Task PreventExplicitCastToVar()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var items = new[] { new { x = 1 } };

                        foreach [||] (var item in items)
                        {
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        var items = new[] { new { x = 1 } };

                        for (int {|Rename:i|} = 0; i < items.Length; i++)
                        {
                            var item = items[i];
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task NotAssignable()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 2, 3 };
                        foreach [||] (string a in array)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestMissingInRegularAndScriptAsync(text);
        }

        [Fact]
        public async Task ElementMissing()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        var array = new int[] { 1, 2, 3 };
                        foreach [||] (in array)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestMissingInRegularAndScriptAsync(text);
        }

        [Fact]
        public async Task ElementMissing2()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        foreach [||] (string a in )
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestMissingInRegularAndScriptAsync(text);
        }

        [Fact]
        public async Task StringExplicitType()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        foreach [||] (int a in "test")
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            var expected = """
                class Test
                {
                    void Method()
                    {
                        string {|Rename:str|} = "test";
                        for (int {|Rename:i|} = 0; i < str.Length; i++)
                        {
                            int a = str[i];
                            Console.WriteLine(a);
                        }
                    }
                }
                """;

            await TestInRegularAndScriptAsync(text, expected);
        }

        [Fact]
        public async Task Var()
        {
            var text = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Explicit();
                        foreach [||] (var a in list)
                        {
                            Console.WriteLine(a);
                        }
                    }
                }

                class Explicit : IReadOnlyList<int>
                {
                    int IReadOnlyList<int>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<int>.Count => throw new NotImplementedException();
                    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
                }
                """;

            var expected = """
                using System;
                using System.Collections;
                using System.Collections.Generic;

                class Test
                {
                    void Method()
                    {
                        var list = new Explicit();
                        var {|Rename:list1|} = (IReadOnlyList<int>)list;
                        for (var {|Rename:i|} = 0; i < list1.Count; i++)
                        {
                            var a = list1[i];
                            Console.WriteLine(a);
                        }
                    }
                }

                class Explicit : IReadOnlyList<int>
                {
                    int IReadOnlyList<int>.this[int index] => throw new NotImplementedException();
                    int IReadOnlyCollection<int>.Count => throw new NotImplementedException();
                    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw new NotImplementedException();
                    IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
                }
                """;
            await TestInRegularAndScriptAsync(text, expected, options: ImplicitTypeEverywhere);
        }

        [Fact]
        public async Task ArrayRank2()
        {
            var text = """
                class Test
                {
                    void Method()
                    {
                        foreach [||] (int a in new int[,] { {1, 2} })
                        {
                            Console.WriteLine(a);
                        }
                    }
                }
                """;
            await TestMissingAsync(text);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48950")]
        public async Task NullableReferenceVar()
        {
            var text = """
                #nullable enable
                class Test
                {
                    void Method()
                    {
                        foreach [||] (var s in new string[10])
                        {
                            Console.WriteLine(s);
                        }
                    }
                }
                """;
            var expected = """
                #nullable enable
                class Test
                {
                    void Method()
                    {
                        var {|Rename:array|} = new string[10];
                        for (var {|Rename:i|} = 0; i < array.Length; i++)
                        {
                            var s = array[i];
                            Console.WriteLine(s);
                        }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(text, expected, options: ImplicitTypeEverywhere);
        }
    }
}
