有没有一种方法可以使用Roslyn确定变量的潜在值?

Jay*_*Jay 5 .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");
            }
}
Run Code Online (Sandbox Code Playgroud)

我可以获取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?");
            }
}
Run Code Online (Sandbox Code Playgroud)

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

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

对于您的第一个方法,它正确地检测到a在第一个之前没有分配if,但肯定在第二个之前分配if

对于你的第二种方法,罗斯林似乎认为a不必分配。但这与 C# 编译器的行为方式一致。例如,以下方法将无法编译:

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

但如果你用 替换第二个ifelse它就会编译。同样,Roslyn 会检测到该变量始终被赋值。