File: EditAndContinue\SyntaxUtilitiesTests.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.
 
#nullable disable
 
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.EditAndContinue;
using Roslyn.Test.Utilities;
using Xunit;
using SyntaxUtilities = Microsoft.CodeAnalysis.CSharp.EditAndContinue.SyntaxUtilities;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue;
 
public class SyntaxUtilitiesTests
{
    private static void VerifySyntaxMap(string oldSource, string newSource)
    {
        var oldRoot = SyntaxFactory.ParseSyntaxTree(oldSource).GetRoot();
        var newRoot = SyntaxFactory.ParseSyntaxTree(newSource).GetRoot();
 
        foreach (var oldNode in oldRoot.DescendantNodes().Where(n => n.FullSpan.Length > 0))
        {
            var newNode = AbstractEditAndContinueAnalyzer.FindPartner(newRoot, oldRoot, oldNode);
            Assert.True(SyntaxFactory.AreEquivalent(oldNode, newNode), $"Node '{oldNode}' not equivalent to '{newNode}'.");
        }
    }
 
    [Fact]
    public void FindPartner1()
    {
        var source1 = @"
using System;
 
class C
{
    static void Main(string[] args)
    {
 
        // sdasd
        var b = true;
        do
        {
            Console.WriteLine(""hi"");
        } while (b == true);
    }
}
";
 
        var source2 = @"
using System;
 
class C
{
    static void Main(string[] args)
    {
        var b = true;
        do
        {
            Console.WriteLine(""hi"");
        } while (b == true);
    }
}
";
        VerifySyntaxMap(source1, source2);
    }
 
    [Fact]
    public void FindLeafNodeAndPartner1()
    {
        var leftRoot = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
class C
{
    public void M()
    {
        if (0 == 1)
        {
            Console.WriteLine(0);
        }
    }
}
").GetRoot();
        var leftPosition = leftRoot.DescendantNodes().OfType<LiteralExpressionSyntax>().ElementAt(2).SpanStart; // 0 within Console.WriteLine(0)
        var rightRoot = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
class C
{
    public void M()
    {
        if (0 == 1)
        {
            if (2 == 3)
            {
                Console.WriteLine(0);
            }
        }
    }
}
").GetRoot();
 
        AbstractEditAndContinueAnalyzer.FindLeafNodeAndPartner(leftRoot, leftPosition, rightRoot, out var leftNode, out var rightNodeOpt);
        Assert.Equal("0", leftNode.ToString());
        Assert.Null(rightNodeOpt);
    }
 
    [Fact]
    public void FindLeafNodeAndPartner2()
    {
        // Check that the method does not fail even if the index of the child (4) 
        // is greater than the count of children on the corresponding (from the upper side) node (3).
        var leftRoot = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
class C
{
    public void M()
    {
        if (0 == 1)
        {
            Console.WriteLine(0);
            Console.WriteLine(1);
            Console.WriteLine(2);
            Console.WriteLine(3);
        }
    }
}
").GetRoot();
 
        var leftPosition = leftRoot.DescendantNodes().OfType<LiteralExpressionSyntax>().ElementAt(5).SpanStart; // 3 within Console.WriteLine(3)
        var rightRoot = SyntaxFactory.ParseSyntaxTree(@"
using System;
 
class C
{
    public void M()
    {
        if (0 == 1)
        {
            if (2 == 3)
            {
                Console.WriteLine(0);
                Console.WriteLine(1);
                Console.WriteLine(2);
                Console.WriteLine(3);
            }
        }
    }
}
").GetRoot();
 
        AbstractEditAndContinueAnalyzer.FindLeafNodeAndPartner(leftRoot, leftPosition, rightRoot, out var leftNode, out var rightNodeOpt);
        Assert.Equal("3", leftNode.ToString());
        Assert.Null(rightNodeOpt);
    }
 
    [Fact]
    public void IsAsyncDeclaration()
    {
        var tree = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    async Task<int> M0() => 1;
    async Task<int> M1() => await Task.FromResult(1);
    async Task<int> M2() { return await Task.FromResult(1); }
 
    void M3()
    {
        async Task<int> f1() => await Task.FromResult(1);
        async Task<int> f2() { return await Task.FromResult(1); }
 
        var l1 = new Func<Task<int>>(async () => await Task.FromResult(1));
        var l2 = new Func<Task<int>>(async () => { return await Task.FromResult(1); });
 
        var l3 = new Func<Task<int>>(async delegate () { return await Task.FromResult(1); });
    }
}
");
 
        var m0 = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.ValueText == "M0");
        var m1 = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.ValueText == "M1");
        var m2 = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.ValueText == "M2");
        var m3 = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.ValueText == "M3");
 
        var f1 = tree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single(m => m.Identifier.ValueText == "f1");
        var f2 = tree.GetRoot().DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single(m => m.Identifier.ValueText == "f2");
 
        var l1 = m3.DescendantNodes().OfType<VariableDeclaratorSyntax>().Single(m => m.Identifier.ValueText == "l1").Initializer.
            DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
 
        var l2 = m3.DescendantNodes().OfType<VariableDeclaratorSyntax>().Single(m => m.Identifier.ValueText == "l2").Initializer.
            DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
 
        var l3 = m3.DescendantNodes().OfType<VariableDeclaratorSyntax>().Single(m => m.Identifier.ValueText == "l3").Initializer.
            DescendantNodes().OfType<AnonymousFunctionExpressionSyntax>().Single();
 
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(m0.ExpressionBody));
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(m1.ExpressionBody));
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(m2));
        Assert.False(SyntaxUtilities.IsAsyncDeclaration(m3));
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(f1.ExpressionBody));
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(f2));
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(l1));
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(l2));
        Assert.True(SyntaxUtilities.IsAsyncDeclaration(l3));
 
        Assert.Equal(0, SyntaxUtilities.GetSuspensionPoints(m0.ExpressionBody).Count());
        Assert.Equal(1, SyntaxUtilities.GetSuspensionPoints(m1.ExpressionBody).Count());
        Assert.Equal(1, SyntaxUtilities.GetSuspensionPoints(m2.Body).Count());
        Assert.Equal(0, SyntaxUtilities.GetSuspensionPoints(m3.Body).Count());
        Assert.Equal(1, SyntaxUtilities.GetSuspensionPoints(f1.ExpressionBody).Count());
        Assert.Equal(1, SyntaxUtilities.GetSuspensionPoints(f2.Body).Count());
        Assert.Equal(1, SyntaxUtilities.GetSuspensionPoints(l1.Body).Count());
        Assert.Equal(1, SyntaxUtilities.GetSuspensionPoints(l2.Body).Count());
        Assert.Equal(1, SyntaxUtilities.GetSuspensionPoints(l3.Body).Count());
    }
 
    [Fact]
    public void GetSuspensionPoints()
    {
        var tree = SyntaxFactory.ParseSyntaxTree(@"
class C
{
    IEnumerable<int> X = new[] { 1, 2, 3 };
 
    IEnumerable<int> M1() { yield return 1; }
    
    void M2() 
    {
        IAsyncEnumerable<int> f() 
        {
            yield return 1;
 
            yield break;
 
            await Task.FromResult(1);
 
            await foreach (var x in F()) { }
 
            await foreach (var (x, y) in F()) { }
 
            await using T x1 = F1(), x2 = F2(), x3 = F3();
        }
    }
}
");
 
        var x = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().Single(m => m.Identifier.ValueText == "X");
        var m1 = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.ValueText == "M1");
        var m2 = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.ValueText == "M2");
        var f = m2.DescendantNodes().OfType<LocalFunctionStatementSyntax>().Single(m => m.Identifier.ValueText == "f");
 
        AssertEx.Empty(SyntaxUtilities.GetSuspensionPoints(x.Initializer));
        AssertEx.Equal(["yield return 1;"], SyntaxUtilities.GetSuspensionPoints(m1.Body).Select(n => n.ToString()));
        AssertEx.Empty(SyntaxUtilities.GetSuspensionPoints(m2.Body));
 
        AssertEx.Equal(
        [
            "yield return 1;",
            "await Task.FromResult(1)",
            "await foreach (var x in F()) { }",
            "await foreach (var (x, y) in F()) { }",
            "x1 = F1()",
            "x2 = F2()",
            "x3 = F3()",
        ], SyntaxUtilities.GetSuspensionPoints(f.Body).Select(n => n.ToString()));
    }
}