有没有办法用Roslyn确定变量的潜在价值?

时间:2012-04-12 19:34:18

标签: .net roslyn

我正在使用Roslyn CTP,我正在尝试确定类中变量的值是否具有值。让我们说我试图检测何时有人使用BinaryExpressionSyntax来确定字符串是否等于什么""。

例如:

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

我可以使用语义和语法来获取BinaryExpressionSyntax并检查左侧和右侧,但我没有在调试器中看到跟踪可能值的任何内容。我知道这可能会变得粗略,例如:

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

是否可以使用Roslyn CTP确定是否已对变量设置了潜在值?我认为这会在StyleCOp / FxCop规则中发挥很大作用。

1 个答案:

答案 0 :(得分:4)

您可以尝试使用SemanticModel.AnalyzeRegionDataFlow()。您给它一个文本范围,它会告诉您有关该文本中数据流的信息,包括哪些变量肯定会在AlwaysAssigned属性中分配。

整个代码(假设您有一个编译单元,而不仅仅是一个方法)可能如下所示:

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

对于您的第一种方法,它会正确检测到a未在第一个if之前分配,但肯定会在第二个if之前分配。

对于你的第二种方法,Roslyn似乎认为a不必分配。但这符合C#编译器的行为方式。例如,以下方法不会编译:

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

但是如果你用if替换第二个else,它将会编译。同样,Roslyn将检测到变量总是被分配。