代码修复:更改用于方法调用的成员和类

时间:2019-01-09 14:42:37

标签: c# visual-studio static-code-analysis roslyn-code-analysis codefixprovider

我正在执行代码分析器,但是在调用方法时找不到改变类和要访问的成员的方法。例如,如果我有

public class FirstClass
{
    public static void DoSomething(object SomeParameter)
    {
        //method body 
    }
}
public class SecondClass
{
    public static void ReplaceDoSomething(object SomeParameter)
    {
        //other method's body
    }
}

我如何使我的代码修复程序从FirstClass.DoSomething(new object());更改为SecondClass.ReplaceDoSomething(new object());

我尝试使用SyntaxFactory,但我无法确定大多数方法预期使用哪些参数,而MSDN几乎没有提供有关它们的详细信息...

代码分析器

public override void Initialize(AnalysisContext context)
{
    context.RegisterOperationAction(Analyze, OperationKind.Invocation);
}
private void Analyze(OperationAnalysisContext context)
{
    IInvocationOperation operation = (IInvocationOperation)context.Operation;
    if (operation.TargetMethod.Name == "DoSomething" && operation.TargetMethod.ContainingNamespace.Name == "FirstNamespace" && operation.TargetMethod.ContainingType.Inherits("FirstNamespace.FirstClass"))
    {
        //Rule is an automaticaly generated variable by visual studio when you create the analyzer
        Diagnostic diagnostic = Diagnostic.Create(Rule, operation.Syntax.GetLocation());
        context.ReportDiagnostic(diagnostic);
    }
}
public static class INamedTypeSymbolExtensions
{
    public static bool Inherits(this INamedTypeSymbol first, string otherFullName)
    {
        if (first.GetFullName() == otherFullName)
        {
            return true;
        }
        else if (typeof(object).FullName == first.GetFullName())
        {
            return false;
        }
        else
        {
            return first.BaseType.Inherits(otherFullName);
        }
    }

    public static string GetFullName(this INamedTypeSymbol element)
    {
        StringBuilder ret = new StringBuilder(element.Name);
        INamespaceSymbol ContainingNamespace = element.ContainingNamespace;
        while (ContainingNamespace.ContainingNamespace != null)
        {
            ret.Insert(0, ContainingNamespace.Name + ".");
            ContainingNamespace = ContainingNamespace.ContainingNamespace;
        }
        return ret.ToString();
    }
}

编辑:我获得的最佳结果是

private async Task<Document> ProvideCodeFix(Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken)
{
    SyntaxToken replaced = invocation.GetFirstToken();
    SyntaxToken replacing = SyntaxFactory.IdentifierName("SecondClass").GetFirstToken().WithLeadingTrivia(replaced.LeadingTrivia));
    return document.WithSyntaxRoot((await document.GetSyntaxRootAsync()).ReplaceToken(replaced, replacing);
}

以此将FirstClass.DoSomething(new object());更改为SecondClass.DoSomething(new object());

但是这种方法的主要问题是,如果我必须将FirstNamespace.FirstClass.DoSomething(new object());更改为SecondClass.ReplaceDoSomething(new object());,那么它将无法正常工作

PS:我使用.WithLeadingTrivia(...)保留评论和在FirstClass之前写的东西

1 个答案:

答案 0 :(得分:1)

此代码有效。我们得到InvocationExpressionSyntax.Expression,它代表()参数列表之前的整个部分。然后我们替换整个表达式。

private async Task<Document> ProvideCodeFix(Document document, InvocationExpressionSyntax invocationExpression, CancellationToken cancellationToken)
{
    // Get the expression that represents the entity that the invocation happens on
    // (In this case this is the method name including possible class name, namespace, etc)
    var invokedExpression = invocationExpression.Expression;

    // Construct an expression for the new classname and methodname
    var classNameSyntax = SyntaxFactory.IdentifierName("SecondClass");
    var methodNameSyntax = SyntaxFactory.IdentifierName("ReplaceDoSomething");
    var classNameAndMethodNameSyntax = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, classNameSyntax, methodNameSyntax);

    // Add the surrounding trivia from the original expression
    var replaced = classNameAndMethodNameSyntax;
    replaced = replaced.WithLeadingTrivia(invokedExpression.GetLeadingTrivia());
    replaced = replaced.WithTrailingTrivia(invokedExpression.GetTrailingTrivia());

    // Replace the whole original expression with the new expression
    return document.WithSyntaxRoot((await document.GetSyntaxRootAsync()).ReplaceNode(invokedExpression, replaced));
}