Kim KangIn
Kim KangIn

Reputation: 349

Roslyn: Analyzing the object invoking the method

For instance:

SqlCommand command = new SqlCommand();
SqlDataReader datareader = command.ExecuteReader();

The invocation node here is command.ExecuteReader(). How can I, using roslyn, get the variable identifier token/node of command from the invocation node? Assuming that this invocation node can have many other method calls before it, e.g. classA.methodA().methodB().classB.methodC(command.ExecuteReader()) and hence getting identifiers through node.DescendantNodes might not be useful. The solution I thought of was to get the SpanStart of ExecuteReader first, then follow by getting the symbol of command by calling SymbolFinder.FindSymbolAtPosition with the position of ExecuteReader.SpanStart - 2. However I am unsure if this solution can handle every single situation. The application I am working on is a static code analyzer.

Upvotes: 4

Views: 2194

Answers (1)

Dan Roberts
Dan Roberts

Reputation: 2329

When you have an invocation node, you can see whether its expression is a member access or not. If the invocation is for a statement "DoThis()" then there isn't a member access but if the invocation is for "x.DoThis()" then there is a member access since "DoThis" is being called against the reference "x".

Once you confirm that there is a member access, you may then get the expression of the target reference - this is the reference whose member is being accessed. This expression may be a simple name identifier (eg. "command") or it may be another member access (eg. "x.command") or it might be another invocation (eg. "GetCommand()") or it might be a combination of these.

To illustrate with code -

private static void AnalyseInvocation(SyntaxNodeAnalysisContext context)
{
    var invocation = (InvocationExpressionSyntax)context.Node;
    var memberAccess = invocation.Expression as MemberAccessExpressionSyntax;
    if ((memberAccess == null) || (memberAccess.Name.Identifier.ValueText != "ExecuteReader"))
        return;

    if (memberAccess.Expression is IdentifierNameSyntax)
    {
        // The target is a simple identifier, the code being analysed is of the form
        // "command.ExecuteReader()" and memberAccess.Expression is the "command"
        // node
    }
    else if (memberAccess.Expression is InvocationExpressionSyntax)
    {
        // The target is another invocation, the code being analysed is of the form
        // "GetCommand().ExecuteReader()" and memberAccess.Expression is the
        // "GetCommand()" node
    }
    else if (memberAccess.Expression is MemberAccessExpressionSyntax)
    {
        // The target is a member access, the code being analysed is of the form
        // "x.Command.ExecuteReader()" and memberAccess.Expression is the "x.Command"
        // node
    }
}

Upvotes: 7

Related Questions