如何使用Roslyn评估局部变量/参数状态

本文关键字:参数 状态 局部变量 评估 何使用 Roslyn | 更新日期: 2023-09-27 17:57:28

我的情况有点复杂。我必须为以下情况创建分析器/代码修复提供程序:只分配了参数但从未使用过,或者从未使用过局部变量。

对于参数情况,我将使用方法声明,并查看参数列表以获取所有分析器。我将遍历方法中的赋值表达式,并筛选使用辅助方法分配的参数。

它变得模糊的地方是我不知道或不知道何时使用了局部变量/参数。我浏览了符号,但它们不能告诉我变量使用了/没有使用。我可以通过将方法声明语法上下文转换为字符串来查找方法中变量名称被提及的次数,并查找已分配的参数,但这只是一个糟糕的想法。

我真的很困,我会从任何有过这种情况经历的人那里得到一些帮助。

对于可能会问的人来说,我主要是在为分析器寻找缺失的逻辑。我不知道代码修复程序将如何工作。如果你知道我能做什么,请随时将其包含在你的答案中!到目前为止,我认为可以从方法中删除未使用的局部变量,也可以从未使用的参数中删除。我现在不确定。

更新

我现在正在尝试使用DataFlow API,但它目前对我不起作用。这个帖子最古老的答案给了我一个起点,但实际上它不起作用。

我想出了自己的办法:

private static bool IsLocalVariableBeingUsed(VariableDeclaratorSyntax variableDeclarator, SyntaxNodeAnalysisContext syntaxNode)
{
    var model = syntaxNode.SemanticModel.Compilation.GetSemanticModel(variableDeclarator.SyntaxTree);
    var methodBody = variableDeclarator.AncestorsAndSelf(false).OfType<MethodDeclarationSyntax>().First();
    var lastMethodNode = methodBody?.ChildNodes().LastOrDefault();
    if (lastMethodNode == null)
        return false;
    var readWrite = syntaxNode.SemanticModel.AnalyzeDataFlow(variableDeclarator, lastMethodNode); 
}

但这也不起作用。使用NUnit测试时:

var input = @"
class TestClass {
    void TestMethod ()
    {
        int i;
    }
}";

当运行时到达readWrite或result(来自最旧的答案)时,我收到以下消息:

System.ArgumentOutRangeException Index was out of range Must be non negative and lesser than the size of the collection"

但在此之前,在我的分析器中,当我尝试验证我的节点以确保它不是null并为数据流API创建适当的元素时,没有代码中断(不确定这是否是适当的术语),但目前我无法继续。

如何使用Roslyn评估局部变量/参数状态

您可以查看是否通过DataFlowAnalysis API使用(读/写)大多数变量。我在博客上写了一篇关于这个API的简介。

我相信在你的案例中,你在寻找从未被阅读过的变量。

var tree = CSharpSyntaxTree.ParseText(@"
public class Sample
{
   public void Foo()
   {
        int unused = 0;
        int used = 1;
        System.Console.Write(used);
   }
}");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
    syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
var methodBody = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single().Body;
DataFlowAnalysis result = model.AnalyzeDataFlow(methodBody);
var variablesDeclared = result.VariablesDeclared;
var variablesRead = result.ReadInside.Union(result.ReadOutside);
var unused = variablesDeclared.Except(variablesRead);
foreach(var variable in unused)
{
    Console.WriteLine(variable);
}

基于JoshVarty的答案,为了使其在诊断中发挥作用,我会为所有MethodDeclaration语法类型注册一个SyntaxNodeAction,然后在主体内部查找未使用的变量:

public override void Initialize(AnalysisContext context)
{
    context.RegisterSyntaxNodeAction(AnalyzeIt, SyntaxKind.MethodDeclaration);
}
private static void AnalyzeIt(SyntaxNodeAnalysisContext context)
{
    var method = context.Node as MethodDeclarationSyntax;
    var dataFlow = context.SemanticModel.AnalyzeDataFlow(method.Body);
    var variablesDeclared = dataFlow.VariablesDeclared;
    var variablesRead = dataFlow.ReadInside.Union(dataFlow.ReadOutside);
    var unused = variablesDeclared.Except(variablesRead);
    if (unused.Any())
    {
        foreach (var unusedVar in unused)
        {
            context.ReportDiagnostic(Diagnostic.Create(Rule, unusedVar.Locations.First()));
        }
    }
}