5

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を取得し、SemanticとSyntaxを使用して左側と右側の両方を調べることができますが、可能な値を追跡するデバッガーには何も表示されません。私はこれが大ざっぱになる可能性があることを知っています例:

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ルールで多くの役割を果たすと思います。

4

1 に答える 1

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、2番目の方法の前に確実に割り当てられifます。

2番目の方法では、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ただし、2番目を。に置き換えるとelse、コンパイルされます。同様に、Roslynは、変数が常に割り当てられていることを検出します。

于 2012-04-12T20:38:51.770 に答える