简体   繁体   中英

How to evaluate local variable/ parameter state with Roslyn

I have a bit of complicated situation. I must create analyzers/ code fix providers for situations such as a parameter is only assigned but never used or local variable are never used.

For the parameter situation, I'm going for the method declaration and looking at the parameter list to get all the analyzer. I'm going through assignment expressions within the method and I filter the parameters that were assigned with an helper method.

Where it gets fuzzy is I have no clue or to know when a local variable/parameter is used or not. I've gone through symbols but they can't tell me that variable used/ not used. I could try to find how many times a variable's name was mentioned inside a method by turning the method declaration syntax context in a string and look for the parameters that were assigned but that's simply such a BAD idea.

I'm really stuck and I would some help for this from anyone who had previous experience with this kind of situation.

For people who might ask, I'm mostly looking for the missing logic for the analyzer. I have no idea how the code fix provider will work. If you have an idea of what I could do, feel free to include it in your answer ! As of now, I was thinking that a local variable that's not used could be deleted from a method and the same could go for an unused parameter. I'm not sure at the moment.

UPDATE

I'm now trying to use the DataFlow API but it's not working for me at the moment. The oldest answer of this thread gave me a starting point but it's actually not working.

I came up with my own way :

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

But this also is not working. When using a test with NUnit :

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

I get the following message when the runtime gets to either readWrite or result(from oldest answer):

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

But before that in my analyzer, when I try to validate my node to make sure it's not null and create the appropriate elements for the data flow API, there's no code break (not sure if that is the appropriate term) but at the moment I cannot progress.

You can see whether or not most variable are used (read/written) via the DataFlowAnalysis APIs. I've written an introduction to this API on my blog .

I believe in your case, you're looking for variables that are never read.

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

Building on JoshVarty's answer, to get this to work in a diagnostic, I would register a SyntaxNodeAction for all MethodDeclaration Syntax Kinds and then look inside the body for unused variables:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM