How can I find previous usages of a variable using Roslyn?

本小妞迷上赌 提交于 2021-02-08 02:57:32

问题


I'm writing a Rosyln analyser/analyzer. It checks to ensure that a method is called before accessing another (potentially dangerous) method on a type. To show what I mean, here's some bad code that I want to analyse and fail on:

private void myMethod()
{
    var myThing = new MyThing();
    myThing.Value = null;

    string value = myThing.GetValue(); // code blows up here as the internal value is null
}

Here's code that's OK because it calls a method that says whether it's null:

private void myMethod()
{
    var myThing = new MyThing();
    myThing.Value = null;

    if(!myThing.HasValue)
    {
        return ;
    }

    string value = myThing.GetValue(); 
}

So, it should check that all calls to GetValue are preceeded by a call to HasValue.

I've just started with Roslyn, so there's probably a more elegant way than my initial (failing) attempt at:

1 - Declare that I want to inspect invocation expressions

context.RegisterSyntaxNodeAction(analyseMemberAccessNode, SyntaxKind.InvocationExpression);

2 - In my method, I get the method name (GetValue())

var expr = (InvocationExpressionSyntax)context.Node;

var memberAccess = expr.Expression as MemberAccessExpressionSyntax;

if (memberAccess?.Name.ToString() != "GetValue")
    return;

3 - I then check to see if it's the right 'GetValue'

var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol as IMethodSymbol;

if (!memberSymbol?.OverriddenMethod.ToString().StartsWith("MyNamespace.MyThing.GetValue") ?? true)
    return;

4 - Up to here, everything is fine. So I get the name of the variable

var e = memberAccess.Expression as IdentifierNameSyntax;

string variableName = e.Identifier.Text;

5 - now I'm stuck - my theory was to; get the containing method, find the single variable declaration that matches variableName, find usages of that, and ensure that HasValue is called before GetValue.

In short, using a Roslyn analyser (deriving from DiagnosticAnalyzer), how do I ensure that HasValue is called before GetValue?


回答1:


Instead of registering for each Invocation, you might be better off registering for the entire method declaration. Then you can keep track of all MemberAccessExpressionSyntax and ensure that for a given variable that HasValue is called before GetValue. To do that, I would get the MemberAccessExpressionSyntax descendants from the MethodDeclaration node.

context.RegisterSyntaxNodeAction((analysisContext) =>
{
    var invocations =
        analysisContext.Node.DescendantNodes().OfType<MemberAccessExpressionSyntax>();
    var hasValueCalls = new HashSet<string>();
    foreach (var invocation in invocations)
    {
        var e = invocation.Expression as IdentifierNameSyntax;

        if (e == null)
            continue;

        string variableName = e.Identifier.Text;

        if (invocation.Name.ToString() == "HasValue")
        {
            hasValueCalls.Add(variableName);
        }

        if (invocation.Name.ToString() == "GetValue")
        {
            if (!hasValueCalls.Contains(variableName))
            {
                analysisContext.ReportDiagnostic(Diagnostic.Create(Rule, e.GetLocation()));
            }
        }
    }
}, SyntaxKind.MethodDeclaration);


来源:https://stackoverflow.com/questions/32076495/how-can-i-find-previous-usages-of-a-variable-using-roslyn

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!