Roslyn Rewrite Method Calls to use await Modifier

473 views Asked by At

I a bunch of C# Files that I want to migrate to a new version of an internal framework we use for development. So far I have been able to accomplish certain things But there is a particular method invocation that i have to modify to use await modifier and the method calls come in different flavors like so

 var a = EntityService.GetBy(Entities.INSTANT_ISSUANCE_REQUEST, requestFilter);
 var b = EntityService.GetBy(Entities.ISSUANCE_SETTINGS, new DejaVuObject()).FirstOrDefault();

I have to modify the above method calls so that they use the following syntax

 var a = await EntityService.GetBy(Entities.INSTANT_ISSUANCE_REQUEST, requestFilter);
 var b = await EntityService.GetBy(Entities.ISSUANCE_SETTINGS, new DejaVuObject()).FirstOrDefault();

I am using Roslyn CSharpSyntaxWriter to traverse the syntax tree.

public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
{
    var variableDeclarations = 
        from i in node.DescendantNodes().OfType<VariableDeclaratorSyntax>()
        where i.DescendantNodes().OfType<EqualsValueClauseSyntax>().Any<EqualsValueClauseSyntax>(
            p => p.DescendantNodes().OfType<InvocationExpressionSyntax>().Any<InvocationExpressionSyntax>())
        select i;

    foreach (var syntax in variableDeclarations)
    {
        var equalsToken = syntax.DescendantNodes();
        //now we have the equals token 
        foreach (var syntaxNode in equalsToken)
        {
            if (syntaxNode is EqualsValueClauseSyntax)
            {
                var equalsValueSyntax = syntaxNode.DescendantNodes().OfType<InvocationExpressionSyntax>();
                foreach (var invocationExpressionSyntax in equalsValueSyntax)
                {
                    var simpleMemberAcessExpressions = invocationExpressionSyntax
                        .DescendantNodes()
                        .OfType<IdentifierNameSyntax>()
                        .Where(i => i.Identifier.Text == "EntityService");
                    foreach (var simpleMemberAcessExpression in simpleMemberAcessExpressions)
                    {
                        var newExpression = $"{invocationExpressionSyntax.ToFullString()}";
                        Console.WriteLine(newExpression);
                        //TODO: Modify the Node
                    }
                }
            }
        }
    }
    return base.VisitLocalDeclarationStatement(node);
}

My problem here is the output in Console is somewhat duplicative

EntityService.GetBy(Entities.ISSUANCE_SETTINGS, new DejaVuObject()).FirstOrDefault();
EntityService.GetBy(Entities.ISSUANCE_SETTINGS, new DejaVuObject())

Both refer to same InvocationExpression but because of the FirstOrDefault() it shows up twice in the console output

I would like for a way to filter the results so that they contain only unique Method Invocations and perform the actual modification of the method call by adding the await modifier and updating the node.

1

There are 1 answers

0
Kris Vandermotten On

first of all, why override VisitLocalDeclarationStatement? Looks like you should be overriding VisitInvocationExpression.

You shouldn't be doing this:

var simpleMemberAcessExpressions = 
    invocationExpressionSyntax.DescendantNodes().OfType<IdentifierNameSyntax>()

Instead, it should be something like:

var identifier = 
    (node.Expression as MemberAccessExpressionSyntax)?.Expression as IdentifierNameSyntax;

if (identifier != null && identifier.Identifier.Text == "EntityService")
{
    // process the invocation expression
}

Assuming the invocation expression is called node, your new expression is probably

var newExpression = 
    SyntaxFactory.ParenthesizedExpression(SyntaxFactory.AwaitExpression(node))
    .WithAdditionalAnnotations(Simplifier.Annotation)
    .WithAdditionalAnnotations(Formatter.Annotation);