﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.ConvertForToForEach;
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.ConvertForToForEach
{
    [Trait(Traits.Feature, Traits.Features.CodeActionsConvertForToForEach)]
    public class ConvertForToForEachTests : AbstractCSharpCodeActionTest
    {
        protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
            => new CSharpConvertForToForEachCodeRefactoringProvider();

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

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

        [Fact]
        public async Task TestArray1()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWarnIfCrossesFunctionBoundary()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Action a = () =>
                            {
                                Console.WriteLine(array[i]);
                            };
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Action a = () =>
                            {
                                Console.WriteLine({|Warning:v|});
                            };
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWarnIfCollectionPotentiallyMutated1()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                            Console.WriteLine(list[i]);
                            list.Add(null);
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (string {|Rename:v|} in list)
                        {
                            Console.WriteLine(v);
                            {|Warning:list|}.Add(null);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWarnIfCollectionPotentiallyMutated2()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                            Console.WriteLine(list[i]);
                            list = null;
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (string {|Rename:v|} in list)
                        {
                            Console.WriteLine(v);
                            {|Warning:list|} = null;
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestNoWarnIfCollectionPropertyAccess()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                            Console.WriteLine(list[i]);
                            Console.WriteLine(list.Count);
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (string {|Rename:v|} in list)
                        {
                            Console.WriteLine(v);
                            Console.WriteLine(list.Count);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestNoWarnIfDoesNotCrossFunctionBoundary()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        Action a = () =>
                        {
                            [||]for (int i = 0; i < array.Length; i++)
                            {
                                Console.WriteLine(array[i]);
                            }
                        };
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        Action a = () =>
                        {
                            foreach (string {|Rename:v|} in array)
                            {
                                Console.WriteLine(v);
                            }
                        };
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMultipleReferences()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestEmbeddedStatement()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                            Console.WriteLine(array[i]);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                            Console.WriteLine(v);
                    }
                }
                """);
        }

        [Fact]
        public async Task TestPostIncrement()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; ++i)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestArrayPlusEqualsIncrementor()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i += 1)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
        public async Task TestBeforeKeyword()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                       [||] for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingAfterOpenParen()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        for ( [||]int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
        public async Task TestInParentheses()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        for ([||]int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingBeforeCloseParen()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        for (int i = 0; i < array.Length; i++[||] )
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")]
        public async Task TestInParentheses2()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        for (int i = 0; i < array.Length; i++[||])
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestAtEndOfFor()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        for[||] (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestForSelected()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [|for|] (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestBeforeOpenParen()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        for [||](int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestAfterCloseParen()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        for (int i = 0; i < array.Length; i++)[||]
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingWithoutIncrementor()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; )
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingWithoutIncorrectIncrementor1()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i += 2)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingWithoutIncorrectIncrementor2()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; j += 2)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingWithoutCondition()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; ; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingWithIncorrectCondition1()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; j < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingWithIncorrectCondition2()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < GetLength(array); i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWithoutInitializer()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWithInitializerOfVariableOutsideLoop()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        int i;
                        [||]for (i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWithUninitializedVariable()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestNotStartingAtZero()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 1; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWithMultipleVariables()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0, j = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestList1()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                            Console.WriteLine(list[i]);
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (string {|Rename:v|} in list)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestChooseNameFromDeclarationStatement()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                            var val = list[i];
                            Console.WriteLine(list[i]);
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (var val in list)
                        {
                            Console.WriteLine(val);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestIgnoreFormattingForReferences()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                            var val = list [ i ];
                            Console.WriteLine(list [ /*find me*/ i ]);
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (var val in list)
                        {
                            Console.WriteLine(val);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestChooseNameFromDeclarationStatement_PreserveComments()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                            // loop comment

                            var val = list[i];
                            Console.WriteLine(list[i]);
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (var val in list)
                        {
                            // loop comment

                            Console.WriteLine(val);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestChooseNameFromDeclarationStatement_PreserveDirectives()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        [||]for (int i = 0; i < list.Count; i++)
                        {
                #if true

                            var val = list[i];
                            Console.WriteLine(list[i]);

                #endif
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    void Test(IList<string> list)
                    {
                        foreach (var val in list)
                        {
                #if true

                            Console.WriteLine(val);

                #endif
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingIfVariableUsedNotForIndexing()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(i);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMissingIfVariableUsedForIndexingNonCollection()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(other[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestWarningIfCollectionWrittenTo()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            array[i] = 1;
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (string {|Rename:v|} in array)
                        {
                            {|Warning:v|} = 1;
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task UseVarIfPreferred1()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        foreach (var {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [Fact]
        public async Task TestDifferentIndexerAndEnumeratorType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class MyList
                {
                  public string this[int i] { get; }

                  public Enumerator GetEnumerator() { }

                  public struct Enumerator { public object Current { get; } }
                }

                class C
                {
                    void Test(MyList list)
                    {
                        // need to use 'string' here to preserve original index semantics.
                        [||]for (int i = 0; i < list.Length; i++)
                        {
                            Console.WriteLine(list[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class MyList
                {
                  public string this[int i] { get; }

                  public Enumerator GetEnumerator() { }

                  public struct Enumerator { public object Current { get; } }
                }

                class C
                {
                    void Test(MyList list)
                    {
                        // need to use 'string' here to preserve original index semantics.
                        foreach (string {|Rename:v|} in list)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [Fact]
        public async Task TestSameIndexerAndEnumeratorType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class MyList
                {
                    public object this[int i] { get => default; }

                    public Enumerator GetEnumerator() { return default; }

                    public struct Enumerator { public object Current { get; } public bool MoveNext() => true; }
                }

                class C
                {
                    void Test(MyList list)
                    {
                        // can use 'var' here since hte type stayed the same.
                        [||]for (int i = 0; i < list.Length; i++)
                        {
                            Console.WriteLine(list[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class MyList
                {
                    public object this[int i] { get => default; }

                    public Enumerator GetEnumerator() { return default; }

                    public struct Enumerator { public object Current { get; } public bool MoveNext() => true; }
                }

                class C
                {
                    void Test(MyList list)
                    {
                        // can use 'var' here since hte type stayed the same.
                        foreach (var {|Rename:v|} in list)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [Fact]
        public async Task TestTrivia()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        // trivia 1
                        [||]for /*trivia 2*/ ( /*trivia 3*/ int i = 0; i < array.Length; i++) /*trivia 4*/
                        // trivia 5
                        {
                            Console.WriteLine(array[i]);
                        } // trivia 6
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        // trivia 1
                        foreach /*trivia 2*/ ( /*trivia 3*/ string {|Rename:v|} in array) /*trivia 4*/
                        // trivia 5
                        {
                            Console.WriteLine(v);
                        } // trivia 6
                    }
                }
                """);
        }

        [Fact]
        public async Task TestNotWithDeconstruction()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[] array)
                    {
                        [||]for (var (i, j) = (0, 0); i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMultidimensionalArray1()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[,] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i, 0]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestMultidimensionalArray2()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[,] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i, i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestJaggedArray1()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        foreach (string[] {|Rename:v|} in array)
                        {
                            Console.WriteLine(v);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestJaggedArray2()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i][0]);
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        foreach (string[] {|Rename:v|} in array)
                        {
                            Console.WriteLine(v[0]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestJaggedArray3()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            var subArray = array[i];
                            for (int j = 0; j < subArray.Length; j++)
                            {
                                Console.WriteLine(array[i][j]);
                            }
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        foreach (var subArray in array)
                        {
                            for (int j = 0; j < subArray.Length; j++)
                            {
                                Console.WriteLine(subArray[j]);
                            }
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestJaggedArray4()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            for (int j = 0; j < array[i].Length; j++)
                            {
                                Console.WriteLine(array[i][j]);
                            }
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        foreach (string[] {|Rename:v|} in array)
                        {
                            for (int j = 0; j < v.Length; j++)
                            {
                                Console.WriteLine(v[j]);
                            }
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestJaggedArray5()
        {
            await TestInRegularAndScript1Async(
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        for (int i = 0; i < array.Length; i++)
                        {
                            [||]for (int j = 0; j < array[i].Length; j++)
                            {
                                Console.WriteLine(array[i][j]);
                            }
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        for (int i = 0; i < array.Length; i++)
                        {
                            foreach (string {|Rename:v|} in array[i])
                            {
                                Console.WriteLine(v);
                            }
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestJaggedArray6()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void Test(string[][] array)
                    {
                        [||]for (int i = 0; i < array.Length; i++)
                        {
                            Console.WriteLine(array[i][i]);
                        }
                    }
                }
                """);
        }

        [Fact]
        public async Task TestDoesNotUseLocalFunctionName()
        {
            await TestInRegularAndScript1Async(
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            [||]for (int i = 0; i < array.Length; i++)
            {
                Console.WriteLine(array[i]);
            }

            void v() { }
        }
    }
    """,
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            foreach (string {|Rename:v1|} in array)
            {
                Console.WriteLine(v1);
            }

            void v() { }
        }
    }
    """);
        }

        [Fact]
        public async Task TestUsesLocalFunctionParameterName()
        {
            await TestInRegularAndScript1Async(
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            [||]for (int i = 0; i < array.Length; i++)
            {
                Console.WriteLine(array[i]);
            }

            void M(string v)
            {
            }
        }
    }
    """,
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            foreach (string {|Rename:v|} in array)
            {
                Console.WriteLine(v);
            }

            void M(string v)
            {
            }
        }
    }
    """);
        }

        [Fact]
        public async Task TestDoesNotUseLambdaParameterWithCSharpLessThan8()
        {
            await TestInRegularAndScript1Async(
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            [||]for (int i = 0; i < array.Length; i++)
            {
                Console.WriteLine(array[i]);
            }

            Action<int> myLambda = v => { };
        }
    }
    """,
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            foreach (string {|Rename:v1|} in array)
            {
                Console.WriteLine(v1);
            }

            Action<int> myLambda = v => { };
        }
    }
    """, parameters: new TestParameters(new CSharpParseOptions(LanguageVersion.CSharp7_3)));
        }

        [Fact]
        public async Task TestUsesLambdaParameterNameInCSharp8()
        {
            await TestInRegularAndScript1Async(
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            [||]for (int i = 0; i < array.Length; i++)
            {
                Console.WriteLine(array[i]);
            }

            Action<int> myLambda = v => { };
        }
    }
    """,
    """
    using System;

    class C
    {
        void Test(string[] array)
        {
            foreach (string {|Rename:v|} in array)
            {
                Console.WriteLine(v);
            }

            Action<int> myLambda = v => { };
        }
    }
    """, parameters: new TestParameters(new CSharpParseOptions(LanguageVersion.CSharp8)));
        }

        [Fact]
        public async Task TestNotWhenIteratingDifferentLists()
        {
            await TestMissingAsync(
                """
                using System;
                using System.Collection.Generic;

                class Item { public string Value; }

                class C
                {
                    static void Test()
                    {
                        var first = new { list = new List<Item>() };
                        var second = new { list = new List<Item>() };

                        [||]for (var i = 0; i < first.list.Count; i++)
                        {
                            first.list[i].Value = second.list[i].Value;
                        }
                    }
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36305")]
        public async Task TestOnElementAt1()
        {
            await TestInRegularAndScript1Async(
                """
                using System;
                using System.Collections.Generic;
                using System.Linq;

                class V
                {
                    void M(ICollection<V> collection)
                    {
                        [||]for (int i = 0; i < collection.Count; ++i)
                            collection.ElementAt(i).M();
                    }

                    private void M()
                    {
                    }
                }
                """,

                """
                using System;
                using System.Collections.Generic;
                using System.Linq;

                class V
                {
                    void M(ICollection<V> collection)
                    {
                        foreach (V {|Rename:v|} in collection)
                            v.M();
                    }

                    private void M()
                    {
                    }
                }
                """);
        }
    }
}
