使用Roslyn CodeAnalyzer

时间:2016-04-30 01:10:37

标签: c# code-analysis roslyn roslyn-code-analysis

小猪退后Add a parameter to a method with a Roslyn CodeFixProvider

我正在努力创建一个CodeFixProvider,以确保所有异步方法都采用CancellationToken

//Before Code Fix:
public async Task Example(){}

//After Code Fix
public async Task Example(CancellationToken token){}

我可以将参数添加到方法中,但我必须使用Type.FullName。相反,我想将System.Threading的using语句添加到类文件的顶部,这样该方法就不需要使用完整的命名空间。换句话说:

// What I have thus far:
public class AClass{
  public async Task Example(System.Threading.CancellationToken token){}
}

// What I want:
using System.Threading;

public class AClass{
  public async Task Example(CancellationToken token){}
}

如何将使用语句添加到Document

我已经尝试了几种方法,但是当我替换SyntaxTree中的多个节点时它的接缝正在丢失(因为树在每次更改时都是不可变的并且重建)。

我能够让它部分地使用下面的代码,但这只有在填充CompilationUnitSyntax.Using属性时才有效,而在命名空间之后使用语句时则不然。这也依赖于文件中至少有一个using语句。

有更好的方法吗?

private async Task<Document> HaveMethodTakeACancellationTokenParameter(
        Document document, SyntaxNode syntaxNode, CancellationToken cancellationToken)
    {
        var syntaxTree = 
            (await document.GetSyntaxTreeAsync(cancellationToken))
                .GetRoot(cancellationToken);

        var method = syntaxNode as MethodDeclarationSyntax;

        #region Add Parameter
        var newParameter =
            SyntaxFactory.Parameter(
                SyntaxFactory.Identifier("cancellationToken")
            )
            .WithType(
                SyntaxFactory.ParseTypeName(
                    typeof(CancellationToken).FullName));

        var updatedMethod = method.AddParameterListParameters(newParameter);

        syntaxTree = syntaxTree.ReplaceNode(method, updatedMethod);

        #endregion

        #region Add Using Statements

        var compilation =
            syntaxTree as CompilationUnitSyntax;                    

        var systemThreadingUsingName =
            SyntaxFactory.QualifiedName(
                SyntaxFactory.IdentifierName("System"),
                SyntaxFactory.IdentifierName("Threading"));

        if (compilation.Usings.All(u => u.Name.GetText().ToString() != typeof(CancellationToken).Namespace))
        {
            syntaxTree = syntaxTree.InsertNodesAfter(compilation.Usings.Last(), new[]
            {
                SyntaxFactory.UsingDirective(
                        systemThreadingUsingName)
            });
        }

        #endregion

        return document.WithSyntaxRoot(syntaxTree);
    }

1 个答案:

答案 0 :(得分:1)

选项是使用注释标记所有方法,添加using语句,使用注释查找方法,更改所有方法并删除注释。

如你所说,树是不可变的,但修改期间注释不会丢失。所以你需要以下内容:

var annotation = new SyntaxAnnotation();
var newRoot = root.ReplaceNode(
     method,
     method.WithAdditionalAnnotations(annotation));
newRoot = AddUsing(newRoot);
method = newRoot.GetAnnotatedNodes(annotation).First();
var newMethod = ChangeParameters(method);
newRoot = root.ReplaceNode(method, newMethod.WithoutAnnotations(annotation));

全面实施:

 private async Task<Document> HaveMethodTakeACancellationTokenParameter(
        Document document, SyntaxNode syntaxNode, CancellationToken cancellationToken)
    {
        var method = syntaxNode as MethodDeclarationSyntax;

        var cancellationTokenParameter =
           SyntaxFactory.Parameter(
               SyntaxFactory.Identifier("cancellationToken")
           )
           .WithType(
               SyntaxFactory.ParseTypeName(
                   typeof(CancellationToken).Name));

        var root = 
           (await document.GetSyntaxTreeAsync(cancellationToken))
               .GetRoot(cancellationToken);

        var annotation = new SyntaxAnnotation();
        var newRoot = root.ReplaceNode(
             method,
             method.WithAdditionalAnnotations(annotation));

        #region Add Using Statements

        var systemThreadingUsingStatement =
            SyntaxFactory.UsingDirective(
                SyntaxFactory.QualifiedName(
                    SyntaxFactory.IdentifierName("System"),
                    SyntaxFactory.IdentifierName("Threading")));

        var compilation =
            newRoot as CompilationUnitSyntax;

        if (null == compilation)
        {
            newRoot =
                newRoot.InsertNodesBefore(
                    newRoot.ChildNodes().First(),
                    new[] {systemThreadingUsingStatement});
        }
        else if (compilation.Usings.All(u => u.Name.GetText().ToString() != typeof(CancellationToken).Namespace))
        {
            newRoot = 
                newRoot.InsertNodesAfter(compilation.Usings.Last(), 
                new[]{ systemThreadingUsingStatement });
        }

        #endregion

        method = (MethodDeclarationSyntax)newRoot.GetAnnotatedNodes(annotation).First();

        var updatedMethod = method.AddParameterListParameters(cancellationTokenParameter);
        newRoot = newRoot.ReplaceNode(method, updatedMethod.WithoutAnnotations(annotation));

        return document.WithSyntaxRoot(newRoot);
    }