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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With