用私有const语句替换字符串文字

时间:2016-06-11 14:48:07

标签: c# roslyn codefixprovider

我正在为C#代码构建一个分析器,当使用字符串文字而不是某些函数的某些参数的const字符串时,会生成错误。即

class MyClass
{
  private void MyMethod(IWriter writer)
  {
    writer.WriteInteger("NamedValue", 4);
  }
}

应该成为:

class MyClass
{
  private const string IoNamedValueKey = "NamedValue";
  private void MyMethod(IWriter writer)
  {
    writer.WriteInteger(IoNamedValueKey , 4);
  }
}

我在显示错误的位置工作,但我也想提供CodeFixProvider。我遇到了两个问题:

  1. 我需要添加private const string IoNamedValueKey = "NamedValue";语句,理想情况就是在有问题的方法之上。
  2. 但前提是它已经存在。
  3. 我不完全确定CodeFixProvider的模板方法是否为我的目的使用了适当的重载(它只是用大写变体替换了类型名称),那么RegisterCodeFixesAsync方法中最好的方法是什么呢?

    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
    {
      // ... now what?
    }
    

    根据roslynquoter的说法,所需的节点可以如下构建,但我仍然对如何将其注入上下文感到茫然。

    CompilationUnit()
    .WithMembers(
        SingletonList<MemberDeclarationSyntax>(
            FieldDeclaration(
                VariableDeclaration(
                    PredefinedType(
                        Token(SyntaxKind.StringKeyword)))
                .WithVariables(
                    SingletonSeparatedList<VariableDeclaratorSyntax>(
                        VariableDeclarator(
                            Identifier("IoNamedValueKey"))
                        .WithInitializer(
                            EqualsValueClause(
                                LiteralExpression(
                                    SyntaxKind.StringLiteralExpression,
                                    Literal("NamedValue")))))))
            .WithModifiers(
                TokenList(
                    new []{
                        Token(SyntaxKind.PrivateKeyword),
                        Token(SyntaxKind.ConstKeyword)}))))
    .NormalizeWhitespace()
    

1 个答案:

答案 0 :(得分:2)

您应该通过CodeAction注册引入已更改文档的context。对于

  • 生成SyntaxNodes - 您可以使用CSharp SyntaxFactory
  • 为您的合作伙伴获取唯一的名称 - 查看Roslyn的UniqueNameGeneratorNameGenerator,它们不会被API公开,但重新实现一些简化版本很容易它们。

以下是您的代码可能类似的示例(已更新):

    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
    {
        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

        var diagnostic = context.Diagnostics.First();
        var diagnosticSpan = diagnostic.Location.SourceSpan;

        var argument = root.FindNode(diagnosticSpan);
        if (!IsBadStringLiteralArgument(argument))
        {
            return;
        }

        // Register a code action that will invoke the fix.
        context.RegisterCodeFix(
            CodeAction.Create(
                title: title,
                createChangedDocument: (ct) => InlineConstField(context.Document, root, argument, ct),
                equivalenceKey: title),
            diagnostic);
    }

    private async Task<Document> InlineConstField(Document document, SyntaxNode root, SyntaxNode argument, CancellationToken cancellationToken)
    {
        var stringLiteral = (argument as ArgumentSyntax).Expression as LiteralExpressionSyntax;
        string suggestdName = this.GetSuggestedName(stringLiteral);
        var containingMember = argument.FirstAncestorOrSelf<MemberDeclarationSyntax>();
        var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
        var containingMemberSymbol = semanticModel.GetDeclaredSymbol(containingMember);


        var takenNames = containingMemberSymbol.ContainingType.MemberNames;
        string uniqueName = this.GetUniqueName(suggestdName, takenNames);
        FieldDeclarationSyntax constField = CreateConstFieldDeclaration(uniqueName, stringLiteral).WithAdditionalAnnotations(Formatter.Annotation);

        var newRoot = root.ReplaceNode(containingMember, new[] { constField, containingMember });
        newRoot = Formatter.Format(newRoot, Formatter.Annotation, document.Project.Solution.Workspace);
        return document.WithSyntaxRoot(newRoot);
    }

    private FieldDeclarationSyntax CreateConstFieldDeclaration(string uniqueName, LiteralExpressionSyntax stringLiteral)
    {
        return SyntaxFactory.FieldDeclaration(
            SyntaxFactory.List<AttributeListSyntax>(),
            SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword)),
            SyntaxFactory.VariableDeclaration(
                SyntaxFactory.ParseTypeName("string"), 
                SyntaxFactory.SingletonSeparatedList(
                    SyntaxFactory.VariableDeclarator(
                        SyntaxFactory.Identifier(uniqueName), 
                        argumentList: null, 
                        initializer: SyntaxFactory.EqualsValueClause(stringLiteral)))));

    }