如何使用Roslyn查找变量的先前用法? [英] How can I find previous usages of a variable using Roslyn?

查看:95
本文介绍了如何使用Roslyn查找变量的先前用法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在写一个Rosyln分析器/分析器.它检查以确保在访问类型上的另一个(潜在危险)方法之前已调用一个方法.为了说明我的意思,下面是一些我想分析并失败的错误代码:

  private void myMethod(){var myThing = new MyThing();myThing.Value = null;字符串值= myThing.GetValue();//由于内部值为null,因此代码在此处爆炸} 

这是可以的代码,因为它调用了一个说明是否为空的方法:

  private void myMethod(){var myThing = new MyThing();myThing.Value = null;if(!myThing.HasValue){返回 ;}字符串值= myThing.GetValue();} 

因此,它应该检查对 GetValue 的所有调用是否先于对 HasValue 的调用.

我刚开始使用罗斯林(Roslyn),所以可能有一种比我最初的尝试(失败)尝试更优雅的方法:

1-声明我要检查调用表达式

  context.RegisterSyntaxNodeAction(analyseMemberAccessNode,SyntaxKind.InvocationExpression); 

2-在我的方法中,我获得了方法名称( GetValue())

  var expr =(InvocationExpressionSyntax)context.Node;var memberAccess = expr.Expression作为MemberAccessExpressionSyntax;如果(memberAccess?.Name.ToString()!="GetValue")返回; 

3-然后检查是否正确的"GetValue"

  var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol作为IMethodSymbol;如果(!memberSymbol?.OverriddenMethod.ToString().StartsWith("MyNamespace.MyThing.GetValue")??是)返回; 

4-到这里,一切都很好.所以我得到了变量

的名称

  var e = memberAccess.Expression作为IdentifierNameSyntax;字符串variableName = e.Identifier.Text; 

5-现在我被困住了-我的理论是:获取包含方法,找到与 variableName 匹配的单个变量声明,查找其用法,并确保在 GetValue 之前调用 HasValue ./p>

简而言之,使用Roslyn分析器(源自 DiagnosticAnalyzer ),如何确保在 GetValue 之前调用 HasValue ?

解决方案

最好为整个方法声明注册,而不是为每个调用注册.然后,您可以跟踪所有 MemberAccessExpressionSyntax ,并确保对于给定的变量,在 GetValue 之前调用 HasValue .为此,我将从 MethodDeclaration 节点获取 MemberAccessExpressionSyntax 后代.

  context.RegisterSyntaxNodeAction((analysisContext)=>{var调用=analysisContext.Node.DescendantNodes().OfType< MemberAccessExpressionSyntax>();var hasValueCalls = new HashSet< string>();foreach(调用中的var调用){var e = invocation.Expression as IdentifierNameSyntax;如果(e == null)继续;字符串variableName = e.Identifier.Text;如果(invocation.Name.ToString()=="HasValue"){hasValueCalls.Add(variableName);}如果(invocation.Name.ToString()=="GetValue"){如果(!hasValueCalls.Contains(variableName)){analysisContext.ReportDiagnostic(Diagnostic.Create(Rule,e.GetLocation()));}}}},SyntaxKind.MethodDeclaration); 

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?

解决方案

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);

这篇关于如何使用Roslyn查找变量的先前用法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆