Roslyn如何查找/修改简化的Linq方法调用

时间:2018-03-12 15:11:36

标签: c# linq roslyn

我们正在使用Roslyn来完成代码库并替换数据库调用,并将新的会话参数添加到之前创建它们的任何方法中。例如:

public bool Save(string someData) { // implementation here }
// Becomes
public bool Save(IDriveSession session, string someData) { }
// And Invocations Go From
Save("MyData");
// to 
Save(session, "MyData");

除了Linq调用之外,这是有效的,特别是那些已经减少的调用。例如:

public IEnumerable<Person> GetPeople(IEnumerable<Guid> ids)
{
    return ids.Select(GetPerson);
}

// Should transform to

public IEnumerable<Person> GetPeople(IDriveSession session, IEnumerable<Guid> ids)
{
    return ids.Select(x => GetPerson(session, x));
}

如果它在foreach循环中,它将正确地看到并转换它。我不确定如何正确看到Linq。

以下是我正在使用的CSharpSyntaxRewriter。

public class DriveSessionMethodReferencesRewriter : CSharpSyntaxRewriter
{
    private readonly SemanticModel semanticModel;
    public DriveSessionMethodReferencesRewriter(SemanticModel semanticModel)
    {
        this.semanticModel = semanticModel ?? throw new ArgumentNullException(nameof(semanticModel));
    }         
    public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) => VisitDeclaration(node); 
    public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node) => VisitDeclaration(node);
    private SyntaxNode VisitDeclaration(BaseMethodDeclarationSyntax node)
    {
        var methodInvocations = RewriterUtility
           .GetMethodInvocationsMissingDriveSessionParameter(semanticModel, node)
           .ToList();

        var objectCreations = RewriterUtility
            .GetObjectCreationsMissingDriveSessionParameter(semanticModel, node)
            .ToList();

        if (!methodInvocations.Any() && !objectCreations.Any()) return node;

        node = node.ReplaceNodes(methodInvocations, (n, ne) => ne.PrependArgument(RewriterUtility.CreateDriveSessionArgument()));
        node = node.ReplaceNodes(objectCreations, (n, ne) => ne.PrependArgument(RewriterUtility.CreateDriveSessionArgument()));

        if (!RewriterUtility.HasDriveSessionParameter(node))
        {
            if (node is ConstructorDeclarationSyntax)
                node = (node as ConstructorDeclarationSyntax).PrependParameter(RewriterUtility.CreateDriveSessionParameter());
            else if (node is MethodDeclarationSyntax)
                node = (node as MethodDeclarationSyntax).PrependParameter(RewriterUtility.CreateDriveSessionParameter());
        }

        return node;
    }
}

适用的静态方法

public static IEnumerable<InvocationExpressionSyntax> GetMethodInvocationsMissingDriveSessionParameter(
        SemanticModel semanticModel, SyntaxNode syntax) => 
            syntax.DescendantNodes()
                .OfType<InvocationExpressionSyntax>()
                .Where(invocationExpressionSyntax => invocationExpressionSyntax.ArgumentList == null || 
                                                    !invocationExpressionSyntax.ArgumentList.Arguments.Any(a => a.ToString() == DriveSessionParameterName))
                .Where(invocationExpressionSyntax => CallsMethodWithDriveSessionParameter(semanticModel, invocationExpressionSyntax));

    // Child Nodes that have method calls with 0 arguments OR without session argument AND the method called takes a new Session Parm.
    // * This may need to be recursive to find nested calls.
    public static IEnumerable<ObjectCreationExpressionSyntax> GetObjectCreationsMissingDriveSessionParameter(
        SemanticModel semanticModel, SyntaxNode syntax) =>
        syntax.DescendantNodes()
            .OfType<ObjectCreationExpressionSyntax>()
            .Where(invocationExpressionSyntax => invocationExpressionSyntax.ArgumentList == null || !invocationExpressionSyntax.ArgumentList.Arguments.Any(a => a.ToString() == DriveSessionParameterName))
            .Where(invocationExpressionSyntax => CallsMethodWithDriveSessionParameter(semanticModel, invocationExpressionSyntax));

    //Determine if methods need session parm.
    private static bool CallsMethodWithDriveSessionParameter(SemanticModel semanticModel, ExpressionSyntax syntax)
    {
        SymbolInfo symbolInfo;
        symbolInfo = semanticModel.GetSymbolInfo(syntax);

        return symbolInfo.CandidateSymbols
            .OfType<IMethodSymbol>()
            .Any(methodSymbol => methodSymbol.Parameters.Any(p => p.Type.Name == DriveSessionTypeName));
    }    

0 个答案:

没有答案