roslynИзмените исходный код на Roslyn

Вступление

Практические примеры использования Roslyn для преобразования исходного кода.

замечания

  • Синтаксические деревья Roslyn неизменяемы. Вызывая метод типа ReplaceNodes, мы генерируем новый узел, а не изменяем существующий. Это требует, чтобы вы всегда меняли объект, над которым работали.

Заменить существующие атрибуты для всех методов в C #, используя дерево синтаксиса

Следующий фрагмент заменяет все атрибуты, называемые PreviousAttribute , атрибутом ReplacementAttribute для всего решения. Образец вручную ищет дерево синтаксиса и заменяет все затронутые узлы.

    static async Task<bool> ModifySolution(string solutionPath)
    {
        using (var workspace = MSBuildWorkspace.Create())
        {
            // Selects a Solution File
            var solution = await workspace.OpenSolutionAsync(solutionPath);
            // Iterates through every project
            foreach (var project in solution.Projects)
            {
                // Iterates through every file
                foreach (var document in project.Documents)
                {
                    // Selects the syntax tree
                    var syntaxTree = await document.GetSyntaxTreeAsync();
                    var root = syntaxTree.GetRoot();
                    // Finds all Attribute Declarations in the Document
                    var existingAttributesList = root.DescendantNodes().OfType<AttributeListSyntax>()
                        // Where the Attribute is declared on a method
                        .Where(curr => curr.Parent is MethodDeclarationSyntax)
                        // And the attribute is named "PreviousAttribute"
                        .Where(curr => curr.Attributes.Any(currentAttribute => currentAttribute.Name.GetText().ToString() == "PreviousAttribute"))
                        .ToList();
                    if (existingAttributesList.Any())
                    {
                        // Generates a replacement for every attribute
                        var replacementAttribute = SyntaxFactory.AttributeList(
                            SyntaxFactory.SingletonSeparatedList(
                            SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("ReplacementAttribute"),
                                SyntaxFactory.AttributeArgumentList(
                                    SyntaxFactory.SeparatedList(new[]
                                    {
                                    SyntaxFactory.AttributeArgument(
                                        SyntaxFactory.LiteralExpression(
                                            SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(@"Sample"))
                                        )
                                    })))));
                        // Replaces all attributes at once.
                        // Note that you should not use root.ReplaceNode
                        // since it would only replace the first note
                        root = root.ReplaceNodes(existingAttributesList, (node, n2) => replacementAttribute);
                        // Exchanges the document in the solution by the newly generated document
                        solution = solution.WithDocumentSyntaxRoot(document.Id, root);
                    }
                }
            }
            // applies the changes to the solution
            var result = workspace.TryApplyChanges(solution);
            return result;
        }
    }

Вышеприведенный пример может быть протестирован для следующего класса:

public class Program
{
    [PreviousAttribute()]
    static void Main(string[] args)
    {
    }
}

Вы не должны использовать Methode root.ReplaceNode для замены нескольких узлов. Поскольку дерево является неизменным, вы будете работать на разных объектах. Использование следующего фрагмента в приведенном выше примере не даст ожидаемого результата:

foreach(var node in existingAttributesList){
    root = root.ReplaceNode(node, replacementAttribute);
}

Первый вызов ReplaceNode создаст новый корневой элемент. Однако элементы в existingAttributesList принадлежат другому корню (предыдущему корневому элементу) и не могут быть заменены из-за этого. Это приведет к замене первого Атрибута, и следующие атрибуты останутся неизменными, поскольку все последовательные вызовы будут выполняться на узле, не присутствующем в новом дереве.

Замените существующие атрибуты для всех методов на C # с помощью SyntaxRewriter

Следующий фрагмент заменяет все атрибуты, называемые «PreviousAttribute» атрибутом «ReplacementAttribute» для всего решения. В образце вручную используется SyntaxRewriter для обмена атрибутами.

/// <summary>
/// The CSharpSyntaxRewriter allows to rewrite the Syntax of a node
/// </summary>
public class AttributeStatementChanger : CSharpSyntaxRewriter
{
    /// Visited for all AttributeListSyntax nodes
    /// The method replaces all PreviousAttribute attributes annotating a method by ReplacementAttribute attributes
    public override SyntaxNode VisitAttributeList(AttributeListSyntax node)
    {
        // If the parent is a MethodDeclaration (= the attribute annotes a method)
        if (node.Parent is MethodDeclarationSyntax &&
            // and if the attribute name is PreviousAttribute
            node.Attributes.Any(
                currentAttribute => currentAttribute.Name.GetText().ToString() == "PreviousAttribute"))
        {
            // Return an alternate node that is injected instead of the current node
            return SyntaxFactory.AttributeList(
                            SyntaxFactory.SingletonSeparatedList(
                            SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("ReplacementAttribute"),
                                SyntaxFactory.AttributeArgumentList(
                                    SyntaxFactory.SeparatedList(new[]
                                    {
                                    SyntaxFactory.AttributeArgument(
                                        SyntaxFactory.LiteralExpression(
                                            SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(@"Sample"))
                                        )
                                    })))));
        }
        // Otherwise the node is left untouched
        return base.VisitAttributeList(node);
    }
}

/// The method calling the Syntax Rewriter
private static async Task<bool> ModifySolutionUsingSyntaxRewriter(string solutionPath)
{
    using (var workspace = MSBuildWorkspace.Create())
    {
        // Selects a Solution File
        var solution = await workspace.OpenSolutionAsync(solutionPath);
        // Iterates through every project
        foreach (var project in solution.Projects)
        {
            // Iterates through every file
            foreach (var document in project.Documents)
            {
                // Selects the syntax tree
                var syntaxTree = await document.GetSyntaxTreeAsync();
                var root = syntaxTree.GetRoot();

                // Generates the syntax rewriter
                var rewriter = new AttributeStatementChanger();
                root = rewriter.Visit(root);

                // Exchanges the document in the solution by the newly generated document
                solution = solution.WithDocumentSyntaxRoot(document.Id, root);
            }
        }
        // applies the changes to the solution
        var result = workspace.TryApplyChanges(solution);
        return result;
    }
}

Вышеприведенный пример может быть протестирован для следующего класса:

public class Program
{
    [PreviousAttribute()]
    static void Main(string[] args)
    {
    }
}