File: Snippets\CSharpForEachSnippetProviderTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.UnitTests)
// 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.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Snippets;
 
[Trait(Traits.Feature, Traits.Features.Snippets)]
public sealed class CSharpForEachSnippetProviderTests : AbstractCSharpSnippetProviderTests
{
    protected override string SnippetIdentifier => "foreach";
 
    [Fact]
    public async Task InsertForEachSnippetInMethodTest()
    {
        await VerifySnippetAsync("""
            class Program
            {
                public void Method()
                {
                    $$
                }
            }
            """, """
            class Program
            {
                public void Method()
                {
                    foreach (var {|0:item|} in {|1:collection|})
                    {
                        $$
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task InsertForEachSnippetInMethodItemUsedTest()
    {
        await VerifySnippetAsync("""
            class Program
            {
                public void Method()
                {
                    var item = 5;
                    $$
                }
            }
            """, """
            class Program
            {
                public void Method()
                {
                    var item = 5;
                    foreach (var {|0:item1|} in {|1:collection|})
                    {
                        $$
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task InsertForEachSnippetInGlobalContextTest()
    {
        await VerifySnippetAsync("""
            $$
            """, """
            foreach (var {|0:item|} in {|1:collection|})
            {
                $$
            }
            """);
    }
 
    [Fact]
    public async Task InsertForEachSnippetInConstructorTest()
    {
        await VerifySnippetAsync("""
            class Program
            {
                public Program()
                {
                    $$
                }
            }
            """, """
            class Program
            {
                public Program()
                {
                    foreach (var {|0:item|} in {|1:collection|})
                    {
                        $$
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task InsertForEachSnippetWithCollectionTest()
    {
        await VerifySnippetAsync("""
            using System.Collections.Generic;
 
            class Program
            {
                public Program()
                {
                    var list = new List<int> { 1, 2, 3 };
                    $$
                }
            }
            """, """
            using System.Collections.Generic;
 
            class Program
            {
                public Program()
                {
                    var list = new List<int> { 1, 2, 3 };
                    foreach (var {|0:item|} in {|1:list|})
                    {
                        $$
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task InsertForEachSnippetInLocalFunctionTest()
    {
        await VerifySnippetAsync("""
            class Program
            {
                public void Method()
                {
                    var x = 5;
                    void LocalMethod()
                    {
                        $$
                    }
                }
            }
            """, """
            class Program
            {
                public void Method()
                {
                    var x = 5;
                    void LocalMethod()
                    {
                        foreach (var {|0:item|} in {|1:collection|})
                        {
                            $$
                        }
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task InsertForEachSnippetInAnonymousFunctionTest()
    {
        await VerifySnippetAsync("""
            public delegate void Print(int value);
 
            static void Main(string[] args)
            {
                Print print = delegate(int val)
                {
                    $$
                };
            }
            """, """
            public delegate void Print(int value);
 
            static void Main(string[] args)
            {
                Print print = delegate(int val)
                {
                    foreach (var {|0:item|} in {|1:args|})
                    {
                        $$
                    }
                };
            }
            """);
    }
 
    [Fact]
    public async Task InsertForEachSnippetInParenthesizedLambdaExpressionTest()
    {
        await VerifySnippetAsync("""
            using System;
 
            Func<int, int, bool> testForEquality = (x, y) =>
            {
                $$
                return x == y;
            };
            """, """
            using System;
 
            Func<int, int, bool> testForEquality = (x, y) =>
            {
                foreach (var {|0:item|} in {|1:args|})
                {
                    $$
                }
                return x == y;
            };
            """);
    }
 
    [Theory]
    [MemberData(nameof(CommonSnippetTestData.CommonEnumerableTypes), MemberType = typeof(CommonSnippetTestData))]
    public async Task InsertInlineForEachSnippetForCorrectTypeTest(string collectionType)
    {
        await VerifySnippetAsync($$"""
            class C
            {
                void M({{collectionType}} enumerable)
                {
                    enumerable.$$
                }
            }
            """, $$"""
            class C
            {
                void M({{collectionType}} enumerable)
                {
                    foreach (var {|0:item|} in enumerable)
                    {
                        $$
                    }
                }
            }
            """);
    }
 
    [Fact]
    public async Task NoInlineForEachSnippetForIncorrectTypeTest()
    {
        await VerifySnippetIsAbsentAsync("""
            class Program
            {
                void M(int arg)
                {
                    arg.$$
                }
            }
            """);
    }
 
    [Fact]
    public async Task NoInlineForEachSnippetWhenNotDirectlyExpressionStatementTest()
    {
        await VerifySnippetIsAbsentAsync("""
            using System;
            using System.Collections.Generic;
 
            class Program
            {
                void M(List<int> list)
                {
                    Console.WriteLine(list.$$);
                }
            }
            """);
    }
 
    [Theory]
    [InlineData("// comment")]
    [InlineData("/* comment */")]
    [InlineData("#region test")]
    public async Task CorrectlyDealWithLeadingTriviaInInlineSnippetInMethodTest1(string trivia)
    {
        await VerifySnippetAsync($$"""
            class Program
            {
                void M(int[] arr)
                {
                    {{trivia}}
                    arr.$$
                }
            }
            """, $$"""
            class Program
            {
                void M(int[] arr)
                {
                    {{trivia}}
                    foreach (var {|0:item|} in arr)
                    {
                        $$
                    }
                }
            }
            """);
    }
 
    [Theory]
    [InlineData("#if true")]
    [InlineData("#pragma warning disable CS0108")]
    [InlineData("#nullable enable")]
    public async Task CorrectlyDealWithLeadingTriviaInInlineSnippetInMethodTest2(string trivia)
    {
        await VerifySnippetAsync($$"""
            class Program
            {
                void M(int[] arr)
                {
            {{trivia}}
                    arr.$$
                }
            }
            """, $$"""
            class Program
            {
                void M(int[] arr)
                {
            {{trivia}}
                    foreach (var {|0:item|} in arr)
                    {
                        $$
                    }
                }
            }
            """);
    }
 
    [Theory]
    [InlineData("// comment")]
    [InlineData("/* comment */")]
    public async Task CorrectlyDealWithLeadingTriviaInInlineSnippetInGlobalStatementTest1(string trivia)
    {
        await VerifySnippetAsync($$"""
            {{trivia}}
            (new int[10]).$$
            """, $$"""
            {{trivia}}
            foreach (var {|0:item|} in new int[10])
            {
                $$
            }
            """);
    }
 
    [Theory]
    [InlineData("#region test")]
    [InlineData("#if true")]
    [InlineData("#pragma warning disable CS0108")]
    [InlineData("#nullable enable")]
    public async Task CorrectlyDealWithLeadingTriviaInInlineSnippetInGlobalStatementTest2(string trivia)
    {
        await VerifySnippetAsync($$"""
            {{trivia}}
            (new int[10]).$$
            """, $$"""

            {{trivia}}
            foreach (var {|0:item|} in new int[10])
            {
                $$
            }
            """);
    }
 
    [Theory]
    [InlineData("")]
    [InlineData("async ")]
    public async Task InsertForEachSnippetAfterSingleAwaitKeywordInMethodBodyTest(string asyncKeyword)
    {
        await VerifySnippetAsync($$"""
            class C
            {
                {{asyncKeyword}}void M()
                {
                    await $$
                }
            }
            """, $$"""
            class C
            {
                {{asyncKeyword}}void M()
                {
                    await foreach (var {|0:item|} in {|1:collection|})
                    {
                        $$
                    }
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Fact]
    public async Task InsertForEachSnippetAfterSingleAwaitKeywordInGlobalStatementTest()
    {
        await VerifySnippetAsync("""
            await $$
            """, """
            await foreach (var {|0:item|} in {|1:collection|})
            {
                $$
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Fact]
    public async Task NoForEachStatementAfterAwaitKeywordWhenWontResultInStatementTest()
    {
        await VerifySnippetIsAbsentAsync("""
            var result = await $$
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Theory]
    [InlineData("")]
    [InlineData("async ")]
    public async Task PreferAsyncEnumerableVariableInScopeForAwaitForEachTest(string asyncKeyword)
    {
        await VerifySnippetAsync($$"""
            using System.Collections.Generic;
            
            class C
            {
                {{asyncKeyword}}void M()
                {
                    IEnumerable<int> enumerable;
                    IAsyncEnumerable<int> asyncEnumerable;
            
                    await $$
                }
            }
            """, $$"""
            using System.Collections.Generic;
            
            class C
            {
                {{asyncKeyword}}void M()
                {
                    IEnumerable<int> enumerable;
                    IAsyncEnumerable<int> asyncEnumerable;
            
                    await foreach (var {|0:item|} in {|1:asyncEnumerable|})
                    {
                        $$
                    }
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Theory]
    [InlineData("")]
    [InlineData("async ")]
    public async Task InsertAwaitForEachSnippetForPostfixAsyncEnumerableTest(string asyncKeyword)
    {
        await VerifySnippetAsync($$"""
            using System.Collections.Generic;
            
            class C
            {
                {{asyncKeyword}}void M(IAsyncEnumerable<int> asyncEnumerable)
                {
                    asyncEnumerable.$$
                }
            }
            """, $$"""
            using System.Collections.Generic;
            
            class C
            {
                {{asyncKeyword}}void M(IAsyncEnumerable<int> asyncEnumerable)
                {
                    await foreach (var {|0:item|} in asyncEnumerable)
                    {
                        $$
                    }
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69598")]
    public async Task InsertInlineForEachSnippetWhenDottingBeforeContextualKeywordTest1()
    {
        await VerifySnippetAsync("""
            using System.Collections.Generic;
 
            class C
            {
                void M(IEnumerable<int> ints)
                {
                    ints.$$
                    var a = 0;
                }
            }
            """, """
            using System.Collections.Generic;
 
            class C
            {
                void M(IEnumerable<int> ints)
                {
                    foreach (var {|0:item|} in ints)
                    {
                        $$
                    }
                    var a = 0;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69598")]
    public async Task InsertInlineForEachSnippetWhenDottingBeforeContextualKeywordTest2()
    {
        await VerifySnippetAsync("""
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                async void M(IEnumerable<int> ints, Task t)
                {
                    ints.$$
                    await t;
                }
            }
            """, """
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                async void M(IEnumerable<int> ints, Task t)
                {
                    foreach (var {|0:item|} in ints)
                    {
                        $$
                    }
                    await t;
                }
            }
            """);
    }
 
    [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/69598")]
    [InlineData("Task")]
    [InlineData("Task<int>")]
    [InlineData("System.Threading.Tasks.Task<int>")]
    public async Task InsertInlineForEachSnippetWhenDottingBeforeNameSyntaxTest(string nameSyntax)
    {
        await VerifySnippetAsync($$"""
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                void M(IEnumerable<int> ints)
                {
                    ints.$$
                    {{nameSyntax}} t = null;
                }
            }
            """, $$"""
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                void M(IEnumerable<int> ints)
                {
                    foreach (var {|0:item|} in ints)
                    {
                        $$
                    }
                    {{nameSyntax}} t = null;
                }
            }
            """);
    }
 
    [Fact]
    public async Task InsertInlineForEachSnippetWhenDottingBeforeMemberAccessExpressionOnTheNextLineTest()
    {
        await VerifySnippetAsync("""
            using System;
 
            class C
            {
                void M(int[] ints)
                {
                    ints.$$
                    Console.WriteLine();
                }
            }
            """, """
            using System;
 
            class C
            {
                void M(int[] ints)
                {
                    foreach (var {|0:item|} in ints)
                    {
                        $$
                    }
                    Console.WriteLine();
                }
            }
            """);
    }
 
    [Fact]
    public async Task NoInlineForEachSnippetWhenDottingBeforeMemberAccessExpressionOnTheSameLineTest()
    {
        await VerifySnippetIsAbsentAsync("""
            using System;
 
            class C
            {
                void M(int[] ints)
                {
                    ints.$$ToString();
                }
            }
            """);
    }
 
    [Fact]
    public async Task NoInlineForEachSnippetWhenDottingBeforeContextualKeywordOnTheSameLineTest()
    {
        await VerifySnippetIsAbsentAsync("""
            using System;
 
            class C
            {
                void M(int[] ints)
                {
                    ints.$$var a = 0;
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69598")]
    public async Task InsertInlineAwaitForEachSnippetWhenDottingBeforeContextualKeywordTest1()
    {
        await VerifySnippetAsync("""
            using System.Collections.Generic;
            
            class C
            {
                void M(IAsyncEnumerable<int> asyncInts)
                {
                    asyncInts.$$
                    var a = 0;
                }
            }
            """, """
            using System.Collections.Generic;
 
            class C
            {
                void M(IAsyncEnumerable<int> asyncInts)
                {
                    await foreach (var {|0:item|} in asyncInts)
                    {
                        $$
                    }
                    var a = 0;
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69598")]
    public async Task InsertInlineAwaitForEachSnippetWhenDottingBeforeContextualKeywordTest2()
    {
        await VerifySnippetAsync("""
            using System.Threading.Tasks;
            using System.Collections.Generic;
            
            class C
            {
                async void M(IAsyncEnumerable<int> asyncInts, Task t)
                {
                    asyncInts.$$
                    await t;
                }
            }
            """, """
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                async void M(IAsyncEnumerable<int> asyncInts, Task t)
                {
                    await foreach (var {|0:item|} in asyncInts)
                    {
                        $$
                    }
                    await t;
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/69598")]
    [InlineData("Task")]
    [InlineData("Task<int>")]
    [InlineData("System.Threading.Tasks.Task<int>")]
    public async Task InsertInlineAwaitForEachSnippetWhenDottingBeforeNameSyntaxTest(string nameSyntax)
    {
        await VerifySnippetAsync($$"""
            using System.Threading.Tasks;
            using System.Collections.Generic;
            
            class C
            {
                void M(IAsyncEnumerable<int> asyncInts)
                {
                    asyncInts.$$
                    {{nameSyntax}} t = null;
                }
            }
            """, $$"""
            using System.Threading.Tasks;
            using System.Collections.Generic;
 
            class C
            {
                void M(IAsyncEnumerable<int> asyncInts)
                {
                    await foreach (var {|0:item|} in asyncInts)
                    {
                        $$
                    }
                    {{nameSyntax}} t = null;
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net70);
    }
 
    [Fact]
    public async Task InsertInlineAwaitForEachSnippetWhenDottingBeforeMemberAccessExpressionOnTheNextLineTest()
    {
        await VerifySnippetAsync("""
            using System;
            using System.Collections.Generic;
 
            class C
            {
                void M(IAsyncEnumerable<int> ints)
                {
                    ints.$$
                    Console.WriteLine();
                }
            }
            """, """
            using System;
            using System.Collections.Generic;
 
            class C
            {
                void M(IAsyncEnumerable<int> ints)
                {
                    await foreach (var {|0:item|} in ints)
                    {
                        $$
                    }
                    Console.WriteLine();
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net80);
    }
 
    [Fact]
    public async Task NoInlineAwaitForEachSnippetWhenDottingBeforeMemberAccessExpressionOnTheSameLineTest()
    {
        await VerifySnippetIsAbsentAsync("""
            using System.Collections.Generic;
 
            class C
            {
                void M(IAsyncEnumerable<int> ints)
                {
                    ints.$$ToString();
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net80);
    }
 
    [Fact]
    public async Task NoInlineAwaitForEachSnippetWhenDottingBeforeContextualKeywordOnTheSameLineTest()
    {
        await VerifySnippetIsAbsentAsync("""
            using System.Collections.Generic;
 
            class C
            {
                void M(IAsyncEnumerable<int> ints)
                {
                    ints.$$var a = 0;
                }
            }
            """,
            referenceAssemblies: ReferenceAssemblies.Net.Net80);
    }
 
    [Theory]
    [MemberData(nameof(CommonSnippetTestData.CommonEnumerableTypes), MemberType = typeof(CommonSnippetTestData))]
    public async Task NoInlineForEachSnippetForTypeItselfTest(string collectionType)
    {
        await VerifySnippetIsAbsentAsync($$"""
            class C
            {
                void M()
                {
                    {{collectionType}}.$$
                }
            }
            """);
    }
 
    [Theory]
    [MemberData(nameof(CommonSnippetTestData.CommonEnumerableTypes), MemberType = typeof(CommonSnippetTestData))]
    public async Task NoInlineForEachSnippetForTypeItselfTest_Parenthesized(string collectionType)
    {
        await VerifySnippetIsAbsentAsync($$"""
            class C
            {
                void M()
                {
                    ({{collectionType}}).$$
                }
            }
            """);
    }
 
    [Theory]
    [MemberData(nameof(CommonSnippetTestData.CommonEnumerableTypes), MemberType = typeof(CommonSnippetTestData))]
    public async Task NoInlineForEachSnippetForTypeItselfTest_BeforeContextualKeyword(string collectionType)
    {
        await VerifySnippetIsAbsentAsync($$"""
            using System.Threading.Tasks;
 
            class C
            {
                async void M()
                {
                    {{collectionType}}.$$
                    await Task.Delay(10);
                }
            }
            """);
    }
 
    [Theory]
    [InlineData("ArrayList")]
    [InlineData("IEnumerable")]
    [InlineData("MyCollection")]
    public async Task InsertInlineForEachSnippetForVariableNamedLikeTypeTest(string typeAndVariableName)
    {
        await VerifySnippetAsync($$"""
            using System.Collections;
            using System.Collections.Generic;
 
            class C
            {
                void M()
                {
                    {{typeAndVariableName}} {{typeAndVariableName}} = default;
                    {{typeAndVariableName}}.$$
                }
            }
 
            class MyCollection : IEnumerable<int>
            {
                public IEnumerator<int> GetEnumerator() => null;
                IEnumerator IEnumerable.GetEnumerator() = null;
            }
            """, $$"""
            using System.Collections;
            using System.Collections.Generic;
            
            class C
            {
                void M()
                {
                    {{typeAndVariableName}} {{typeAndVariableName}} = default;
                    foreach (var {|0:item|} in {{typeAndVariableName}})
                    {
                        $$
                    }
                }
            }
            
            class MyCollection : IEnumerable<int>
            {
                public IEnumerator<int> GetEnumerator() => null;
                IEnumerator IEnumerable.GetEnumerator() = null;
            }
            """);
    }
}