File: Editing\SyntaxEditorTests.cs
Web Access
Project: src\src\Workspaces\CoreTest\Microsoft.CodeAnalysis.Workspaces.UnitTests.csproj (Microsoft.CodeAnalysis.Workspaces.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.Threading;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Editing;
 
[UseExportProvider]
public sealed class SyntaxEditorTests
{
    private Workspace _emptyWorkspace;
 
    private Workspace EmptyWorkspace
        => _emptyWorkspace ??= new AdhocWorkspace();
 
    private void VerifySyntax<TSyntax>(SyntaxNode node, string expectedText) where TSyntax : SyntaxNode
    {
        Assert.IsAssignableFrom<TSyntax>(node);
 
        var options = CSharpSyntaxFormattingOptions.Default;
        var formatted = Formatter.Format(node, EmptyWorkspace.Services.SolutionServices, options, CancellationToken.None);
        var actualText = formatted.ToFullString();
        Assert.Equal(expectedText, actualText);
    }
 
    private SyntaxEditor GetEditor(SyntaxNode root)
        => new SyntaxEditor(root, EmptyWorkspace.Services.SolutionServices);
 
    [Fact]
    public void TestReplaceNode()
    {
        var code = @"
public class C
{
    public int X;
}";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var fieldX = editor.Generator.GetMembers(cls)[0];
        editor.ReplaceNode(fieldX, editor.Generator.FieldDeclaration("Y", editor.Generator.TypeExpression(SpecialType.System_String), Accessibility.Public));
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot,
            @"
public class C
{
    public string Y;
}");
    }
 
    [Fact]
    public void TestRemoveNode()
    {
        var code = @"
public class C
{
    public int X;
}";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var fieldX = editor.Generator.GetMembers(cls)[0];
        editor.RemoveNode(fieldX);
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot,
            @"
public class C
{
}");
    }
 
    [Fact]
    public void TestInsertAfter()
    {
        var code = @"
public class C
{
    public int X;
}";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var fieldX = editor.Generator.GetMembers(cls)[0];
        editor.InsertAfter(fieldX, editor.Generator.FieldDeclaration("Y", editor.Generator.TypeExpression(SpecialType.System_String), Accessibility.Public));
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot,
            @"
public class C
{
    public int X;
    public string Y;
}");
    }
 
    [Fact]
    public void TestInsertBefore()
    {
        var code = @"
public class C
{
    public int X;
}";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var fieldX = editor.Generator.GetMembers(cls)[0];
        editor.InsertBefore(fieldX, editor.Generator.FieldDeclaration("Y", editor.Generator.TypeExpression(SpecialType.System_String), Accessibility.Public));
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot,
            @"
public class C
{
    public string Y;
    public int X;
}");
    }
 
    [Fact]
    public void TestTrackNode()
    {
        var code = @"
public class C
{
    public int X;
}";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var fieldX = editor.Generator.GetMembers(cls)[0];
        editor.TrackNode(fieldX);
        var newRoot = editor.GetChangedRoot();
 
        var currentFieldX = newRoot.GetCurrentNode(fieldX);
        Assert.NotNull(currentFieldX);
    }
 
    [Fact]
    public void TestMultipleEdits()
    {
        var code = @"
public class C
{
    public int X;
}";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var fieldX = editor.Generator.GetMembers(cls)[0];
        editor.InsertAfter(fieldX, editor.Generator.FieldDeclaration("Y", editor.Generator.TypeExpression(SpecialType.System_String), Accessibility.Public));
        editor.InsertBefore(fieldX, editor.Generator.FieldDeclaration("Z", editor.Generator.TypeExpression(SpecialType.System_Object), Accessibility.Public));
        editor.RemoveNode(fieldX);
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot,
            @"
public class C
{
    public object Z;
    public string Y;
}");
    }
 
    [Fact]
    public void TestAddAttribute()
    {
        var code = """
using System;
using System.CodeAnalysis;
 
public class C
{
    Type Main(Type t)
    {
    }
}
""";
        var fixedCode = """
using System;
using System.CodeAnalysis;
 
public class C
{
    Type Main([Example(Sample.Attribute)] Type t)
    {
    }
}
""";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var methodX = (MethodDeclarationSyntax)editor.Generator.GetMembers(cls)[0];
 
        var param = methodX.ParameterList.Parameters[0];
 
        var syntaxGenerator = editor.Generator;
        var args = new[] { syntaxGenerator.AttributeArgument(syntaxGenerator.MemberAccessExpression(syntaxGenerator.DottedName("Sample"), "Attribute")) };
        var attribute = syntaxGenerator.Attribute("Example", args);
        editor.AddAttribute(param, attribute);
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot, fixedCode);
    }
 
    [Fact]
    public void TestAddGenericAttribute()
    {
        var code = """
using System;
using System.CodeAnalysis;
 
public class C
{
    Type Main<T>()
    {
    }
}
""";
        var fixedCode = """
using System;
using System.CodeAnalysis;
 
public class C
{
    Type Main<[Example(Sample.Attribute)] T>()
    {
    }
}
""";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var methodX = (MethodDeclarationSyntax)editor.Generator.GetMembers(cls)[0];
 
        var typeParam = methodX.TypeParameterList.Parameters[0];
 
        var syntaxGenerator = editor.Generator;
        var args = new[] { syntaxGenerator.AttributeArgument(syntaxGenerator.MemberAccessExpression(syntaxGenerator.DottedName("Sample"), "Attribute")) };
        var attribute = syntaxGenerator.Attribute("Example", args);
        editor.AddAttribute(typeParam, attribute);
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot, fixedCode);
    }
 
    [Fact]
    public void TestAddReturnAttribute()
    {
        var code = """
using System;
using System.CodeAnalysis;
 
public class C
{
    Type Main(Type t)
    {
    }
}
""";
        var fixedCode = """
using System;
using System.CodeAnalysis;
 
public class C
{
    [return: Example(Sample.Attribute)]
    Type Main(Type t)
    {
    }
}
""";
 
        var cu = SyntaxFactory.ParseCompilationUnit(code);
        var cls = cu.Members[0];
 
        var editor = GetEditor(cu);
        var methodX = editor.Generator.GetMembers(cls)[0];
 
        var syntaxGenerator = editor.Generator;
        var args = new[] { syntaxGenerator.AttributeArgument(syntaxGenerator.MemberAccessExpression(syntaxGenerator.DottedName("Sample"), "Attribute")) };
        var attribute = syntaxGenerator.Attribute("Example", args);
        editor.AddReturnAttribute(methodX, attribute);
        var newRoot = editor.GetChangedRoot();
 
        VerifySyntax<CompilationUnitSyntax>(
            newRoot, fixedCode);
    }
}