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

Kev*_*non 1 c# analyzer roslyn

我有点复杂的情况.我必须创建分析器/代码修复提供程序,例如仅分配参数但从未使用过,或者从不使用本地变量.

对于参数情况,我要进行方法声明并查看参数列表以获取所有分析器.我正在遍历方法中的赋值表达式,并且我过滤了使用辅助方法分配的参数.

它变得模糊的地方我不知道何时使用局部变量/参数.我已经完成了符号,但他们无法告诉我使用/未使用的变量.我可以尝试通过在字符串中转换方法声明语法上下文来查找方法中提到变量名称的次数,并查找已分配的参数,但这只是一个很糟糕的想法.

我真的被困住了,我会从那些曾经遇到过这种情况的人那里得到一些帮助.

对于可能会问的人,我主要是在寻找分析仪缺失的逻辑.我不知道代码修复提供程序将如何工作.如果您对我能做的事情有所了解,请随意将其包含在您的答案中!截至目前,我认为可以从方法中删除未使用的局部变量,同样可以使用未使用的参数.我现在不确定.

UPDATE

我现在正在尝试使用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); 
}
Run Code Online (Sandbox Code Playgroud)

但这也行不通.使用NUnit测试时:

var input = @"
class TestClass {
    void TestMethod ()
    {
        int i;
    }
}";
Run Code Online (Sandbox Code Playgroud)

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

System.ArgumentOutRangeException Index was out of range Must be non negative and lesser than the size of the collection"
Run Code Online (Sandbox Code Playgroud)

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

Jos*_*rty 8

您可以通过DataFlowAnalysisAPI 查看是否使用(读/写)大多数变量.我在我的博客上写了这个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);
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ner 5

在JoshVarty的回答的基础上,为了让它在诊断中工作,我会SyntaxNodeAction为所有MethodDeclaration语法类型注册一个,然后查看正文内部未使用的变量:

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()));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)