El análisis de flujo de control se utiliza para comprender los diversos puntos de entrada y salida dentro de un bloque de código y para responder preguntas sobre accesibilidad. Si estamos analizando un método, es posible que nos interesen todos los puntos return . Si estamos analizando un bucle for, es posible que nos interesen todos los lugares en los que hacemos break o continue.
Activamos el análisis de flujo de control a través de un método de SemanticModel. Esto nos devuelve una instancia de ControlFlowAnalysis que expone las siguientes propiedades:
EntryPoints: el conjunto de declaraciones dentro de la región que son el destino de las sucursales fuera de la región.
ExitPoints: el conjunto de declaraciones dentro de una región que salta a ubicaciones fuera de la región.
EndPointIsReachable: indica si una región se completa normalmente. Devuelve verdadero si y solo si se puede llegar al final de la última declaración o si toda la región no contiene declaraciones.
StartPointIsReachable: indica si una región puede comenzar normalmente.
ReturnStatements: el conjunto de declaraciones de devolución dentro de una región.
Succeeded: devuelve verdadero si y solo si el análisis fue exitoso. El análisis puede fallar si la región no abarca correctamente una sola expresión, una sola declaración o una serie contigua de declaraciones dentro del bloque adjunto.
Uso básico de la API:
var tree = CSharpSyntaxTree.ParseText(@"
class C
{
void M()
{
for (int i = 0; i < 10; i++)
{
if (i == 3)
continue;
if (i == 8)
break;
}
}
}
");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
var firstFor = tree.GetRoot().DescendantNodes().OfType<ForStatementSyntax>().Single();
ControlFlowAnalysis result = model.AnalyzeControlFlow(firstFor.Statement);
Console.WriteLine(result.Succeeded); //True
Console.WriteLine(result.ExitPoints.Count()); //2 – continue, and break
Alternativamente, podemos especificar dos declaraciones y analizar las declaraciones entre los dos. El siguiente ejemplo demuestra esto y el uso de EntryPoints:
var tree = CSharpSyntaxTree.ParseText(@"
class C
{
void M(int x)
{
L1: ; // 1
if (x == 0) goto L1; //firstIf
if (x == 1) goto L2;
if (x == 3) goto L3;
L3: ; //label3
L2: ; // 2
if(x == 4) goto L3;
}
}
");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
//Choose first and last statements
var firstIf = tree.GetRoot().DescendantNodes().OfType<IfStatementSyntax>().First();
var label3 = tree.GetRoot().DescendantNodes().OfType<LabeledStatementSyntax>().Skip(1).Take(1).Single();
ControlFlowAnalysis result = model.AnalyzeControlFlow(firstIf, label3);
Console.WriteLine(result.EntryPoints); //1 – Label 3 is a candidate entry point within these statements
Console.WriteLine(result.ExitPoints); //2 – goto L1 and goto L2 and candidate exit points
En el ejemplo anterior, vemos un ejemplo de una posible etiqueta de punto de entrada L3. Que yo sepa, las etiquetas son los únicos puntos de entrada posibles.
Finalmente, veremos cómo responder preguntas sobre la accesibilidad. A continuación, no se puede alcanzar ni el punto inicial ni el punto final:
var tree = CSharpSyntaxTree.ParseText(@"
class C
{
void M(int x)
{
return;
if(x == 0) //-+ Start is unreachable
System.Console.WriteLine(""Hello""); // |
L1: //-+ End is unreachable
}
}
");
var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);
//Choose first and last statements
var firstIf = tree.GetRoot().DescendantNodes().OfType<IfStatementSyntax>().Single();
var label1 = tree.GetRoot().DescendantNodes().OfType<LabeledStatementSyntax>().Single();
ControlFlowAnalysis result = model.AnalyzeControlFlow(firstIf, label1);
Console.WriteLine(result.StartPointIsReachable); //False
Console.WriteLine(result.EndPointIsReachable); //False
En general, la API de flujo de control parece mucho más intuitiva que la API de análisis de flujo de datos. Requiere menos conocimiento de la especificación de C# y es sencillo trabajar con él.