Is there a way to determine the potential value of a variable using Roslyn?

.net, roslyn

Solution

You could try to use `SemanticModel.AnalyzeRegionDataFlow()` for this. You give it a text span and it tells you information about the dataflow in that piece of text, including what variables are sure to be assigned in the `AlwaysAssigned` property.

The whole code (assuming you have a compilation unit, not just a method) could look like this:

var tree = SyntaxTree.ParseCompilationUnit(code);

var compilation = Compilation.Create("foo")
    .AddSyntaxTrees(tree);

var semanticModel = compilation.GetSemanticModel(tree);

var methods = tree.Root.DescendentNodes().OfType<MethodDeclarationSyntax>();

foreach (var method in methods)
{
    Console.WriteLine(method.Identifier.ValueText);

    var binaryExpressions = method.DescendentNodes()
        .OfType<BinaryExpressionSyntax>()
        .Where(e => e.Kind == SyntaxKind.EqualsExpression);

    foreach (var binaryExpression in binaryExpressions)
    {
        Console.WriteLine(binaryExpression);

        // get TextSpan that starts at the beginning of the method body
        // and ends at the beginning of the binary expression
        var textBefore = TextSpan.FromBounds(
            method.BodyOpt.Span.Start, binaryExpression.Span.Start);

        //Console.WriteLine(tree.Root.GetFullTextAsIText().GetText(textBefore));

        var alwaysAssigned = semanticModel.AnalyzeRegionDataFlow(textBefore)
            .AlwaysAssigned;

        var isAAlwaysAssigned = alwaysAssigned.Any(s => s.Name == "a");

        Console.WriteLine(isAAlwaysAssigned);
    }

    Console.WriteLine();
}

For your first method, it correctly detects that `a` wasn't assigned before the first `if`, but is certainly assigned before the second `if`.

For your second method, Roslyn seems to think that `a` doesn't have to be assigned. But that is in line with how the C# compiler behaves. For example, the following method won't compile:

private void BooleanTest(bool b)
{
    string a;
    if (b)
        a = "";
    if (!b)
        a = "42";
    if (a == "")
        Console.WriteLine("What Do I Do?");
}

But if you replace the second `if` with `else`, it will compile. And similarly, Roslyn will detect that the variable is always assigned.

Problem

I am using the Roslyn CTP and I am trying to determine if the value of a variable in a class has a value. Lets say I am trying to detect when someone is using a BinaryExpressionSyntax to determine if a string is equal to nothing "". For example: ``` private void StringLiteral(string a) { if (a == "") //flagged because we do not see a explicit set of 'a' { Console.WriteLine("Empty String"); } a="42"; if (a == "") //not flagged because 'a' has been set { Console.WriteLine("Empty String"); } } ``` I can get the BinaryExpressionSyntax and examine both the left and right sides using Semantic and Syntax but I don't see anything in the debugger that tracks the possible value. I know this could get sketchy e.g.: ``` private void BooleanTest(string a, bool b) { if (b) { a=""; } if (!b) { a="42"; } if (a == "") // Maybe 'a' is set maybe it isn't so we will probably not flag this one { Console.WriteLine("What Do I Do?"); } } ``` Is it possible with the Roslyn CTP to determine if a potential value has been set on a variable? I would think that this would come into play a lot in the StyleCOp/FxCop rules.

Original source