File: Compilation\ForEachStatementInfoTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.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.
 
#nullable disable
 
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    public class ForEachStatementInfoTests : CSharpTestBase
    {
        [Fact]
        public void Equality()
        {
            var c = (Compilation)CreateCompilation(@"
class E1
{
    public E GetEnumerator() { return null; }
    public bool MoveNext() { return false; }
    public object Current { get; }
    public void Dispose() { }
}
 
class E2
{
    public E GetEnumerator() { return null; }
    public bool MoveNext() { return false; }
    public object Current { get; }
    public void Dispose() { }
}
");
            var e1 = (ITypeSymbol)c.GlobalNamespace.GetMembers("E1").Single();
            var ge1 = (IMethodSymbol)e1.GetMembers("GetEnumerator").Single();
            var mn1 = (IMethodSymbol)e1.GetMembers("MoveNext").Single();
            var cur1 = (IPropertySymbol)e1.GetMembers("Current").Single();
            var disp1 = (IMethodSymbol)e1.GetMembers("Dispose").Single();
            var conv1 = Conversion.Identity;
            var mnea1 = default(AwaitExpressionInfo);
            var dispa1 = default(AwaitExpressionInfo);
 
            var e2 = (ITypeSymbol)c.GlobalNamespace.GetMembers("E2").Single();
            var ge2 = (IMethodSymbol)e2.GetMembers("GetEnumerator").Single();
            var mn2 = (IMethodSymbol)e2.GetMembers("MoveNext").Single();
            var cur2 = (IPropertySymbol)e2.GetMembers("Current").Single();
            var disp2 = (IMethodSymbol)e2.GetMembers("Dispose").Single();
            var conv2 = Conversion.NoConversion;
            var mnea2 = new AwaitExpressionInfo(mn2, null, null, null, false);
            var dispa2 = new AwaitExpressionInfo(disp2, null, null, null, false);
 
            EqualityTesting.AssertEqual(default(ForEachStatementInfo), default(ForEachStatementInfo));
            EqualityTesting.AssertEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge2, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn2, mnea1, cur1, disp1, dispa1, e1, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea2, cur1, disp1, dispa1, e1, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur2, disp1, dispa1, e1, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp2, dispa1, e1, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa2, e1, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e2, conv1, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv2, conv1), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv2), new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
            EqualityTesting.AssertNotEqual(new ForEachStatementInfo(isAsync: true, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1), new ForEachStatementInfo(isAsync: false, ge1, mn1, mnea1, cur1, disp1, dispa1, e1, conv1, conv1));
        }
 
        [Fact]
        public void TestAwaitForeachAwaitableInfo()
        {
            var text = @"
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
 
class C
{
    async Task M()
    {
        await foreach (var x in new AsyncEnumerable())
        {
        }
    }
}
 
class AsyncEnumerable : IAsyncEnumerable<int>
{
    public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default)
        => new AsyncEnumerator();
}
 
class AsyncEnumerator : IAsyncEnumerator<int>
{
    public int Current => 42;
    
    public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(false);
    
    public ValueTask DisposeAsync() => default;
}";
 
            var comp = CreateCompilationWithTasksExtensions(new[] { text, s_IAsyncEnumerable });
            validate(comp, isRuntimeAsync: false);
 
            comp = CreateRuntimeAsyncCompilation(text);
            validate(comp, isRuntimeAsync: true);
 
            static void validate(CSharpCompilation comp, bool isRuntimeAsync)
            {
                comp.VerifyDiagnostics();
                var tree = comp.SyntaxTrees.First();
                var model = comp.GetSemanticModel(tree);
 
                var awaitForeachStatement = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
 
                var info = model.GetForEachStatementInfo(awaitForeachStatement);
                Assert.True(info.IsAsynchronous);
 
                // Verify MoveNextAwaitableInfo
                var moveNextAwaitInfo = info.MoveNextAwaitableInfo;
                if (isRuntimeAsync)
                {
                    Assert.Null(moveNextAwaitInfo.GetAwaiterMethod);
                    Assert.Null(moveNextAwaitInfo.IsCompletedProperty);
                    Assert.Null(moveNextAwaitInfo.GetResultMethod);
                    AssertEx.Equal("System.Boolean System.Runtime.CompilerServices.AsyncHelpers.Await<System.Boolean>(System.Threading.Tasks.ValueTask<System.Boolean> task)",
                        moveNextAwaitInfo.RuntimeAwaitMethod.ToTestDisplayString());
                }
                else
                {
                    AssertEx.Equal("System.Runtime.CompilerServices.ValueTaskAwaiter<System.Boolean> System.Threading.Tasks.ValueTask<System.Boolean>.GetAwaiter()",
                        moveNextAwaitInfo.GetAwaiterMethod.ToTestDisplayString());
                    Assert.NotNull(moveNextAwaitInfo.IsCompletedProperty);
                    Assert.NotNull(moveNextAwaitInfo.GetResultMethod);
                    Assert.Null(moveNextAwaitInfo.RuntimeAwaitMethod);
                }
                Assert.False(moveNextAwaitInfo.IsDynamic);
 
                // Verify DisposeAwaitableInfo  
                var disposeAwaitInfo = info.DisposeAwaitableInfo;
                if (isRuntimeAsync)
                {
                    Assert.Null(disposeAwaitInfo.GetAwaiterMethod);
                    Assert.Null(disposeAwaitInfo.IsCompletedProperty);
                    Assert.Null(disposeAwaitInfo.GetResultMethod);
                    AssertEx.Equal("void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask task)",
                        disposeAwaitInfo.RuntimeAwaitMethod.ToTestDisplayString());
                }
                else
                {
                    AssertEx.Equal("System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()",
                        disposeAwaitInfo.GetAwaiterMethod.ToTestDisplayString());
                    Assert.NotNull(disposeAwaitInfo.IsCompletedProperty);
                    Assert.NotNull(disposeAwaitInfo.GetResultMethod);
                    Assert.Null(disposeAwaitInfo.RuntimeAwaitMethod);
                }
                Assert.False(disposeAwaitInfo.IsDynamic);
            }
        }
 
        [Fact]
        public void TestSynchronousForeachAwaitableInfo()
        {
            var text = @"
using System.Collections.Generic;
 
class C
{
    void M()
    {
        foreach (var x in new List<int>())
        {
        }
    }
}";
 
            var comp = CreateCompilation(text);
            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);
 
            var foreachStatement = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
            var info = model.GetForEachStatementInfo(foreachStatement);
 
            Assert.False(info.IsAsynchronous);
 
            var moveNextAwaitInfo = info.MoveNextAwaitableInfo;
            Assert.Null(moveNextAwaitInfo.GetAwaiterMethod);
            Assert.Null(moveNextAwaitInfo.IsCompletedProperty);
            Assert.Null(moveNextAwaitInfo.GetResultMethod);
            Assert.False(moveNextAwaitInfo.IsDynamic);
 
            var disposeAwaitInfo = info.DisposeAwaitableInfo;
            Assert.Null(disposeAwaitInfo.GetAwaiterMethod);
            Assert.Null(disposeAwaitInfo.IsCompletedProperty);
            Assert.Null(disposeAwaitInfo.GetResultMethod);
            Assert.False(disposeAwaitInfo.IsDynamic);
        }
    }
}