|
// 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()));
}
}
|