3

I can't find a way to construct a control flow graph for c# code using Roslyn.

I know there is a namespace in the Roslyn compiler called "Microsoft.CodeAnalysis.FlowAnalysis" that contains some classes to create a control flow graph but I don't know how to use it.

https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.flowanalysis?view=roslyn-dotnet

there is a class called ControlFlowGraph.cs but the problem i can't create an object or a subclass from this class. https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.flowanalysis.controlflowgraph?view=roslyn-dotnet

please if anyone knows how to use this namespace to construct a control flow graph or if there is an example to use. thank you

Hamza
  • 440
  • 7
  • 20

2 Answers2

4

I have manage to create the CFG from a method node:

CSharpParseOptions options = CSharpParseOptions.Default
    .WithFeatures(new[] { new KeyValuePair<string, string>("flow-analysis", "")     
});

MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution solution = workspace.OpenSolutionAsync(solutionUrl).Result; // path to your SLN file

ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
Dictionary<string, Stream> assemblies = new Dictionary<string, Stream>();

var projects = projectGraph.GetTopologicallySortedProjects().ToDictionary(
    p => p,
    p => solution.GetProject(p).Name);

var bllProjectId = projects.First(p => p.Value == "<your project name>").Key; // choose project for analysis
var projectId = bllProjectId;
solution = solution.WithProjectParseOptions(projectId, options);

Compilation compilation = solution.GetProject(projectId).GetCompilationAsync().Result;
if (compilation != null && !string.IsNullOrEmpty(compilation.AssemblyName))
{
    var syntaxTree = compilation.SyntaxTrees.First();

    // get syntax nodes for methods
    var methodNodes = from methodDeclaration in syntaxTree.GetRoot().DescendantNodes()
            .Where(x => x is MethodDeclarationSyntax)
        select methodDeclaration;

    foreach (MethodDeclarationSyntax node in methodNodes)
    {
        var model = compilation.GetSemanticModel(node.SyntaxTree);
        node.Identifier.ToString().Dump();
        if (node.SyntaxTree.Options.Features.Any())
        {
            var graph = ControlFlowGraph.Create(node, model); // CFG is here
        }
        else
        {
            // "No features".Dump();
        }
    }
}

The next step will be anaylysis of the CFG ...

Karel

Karel Frajták
  • 4,389
  • 1
  • 23
  • 34
  • Hey, thanks for your response I encountered a problem in this code so I tried to follow the same logic but without using workspace (MSBuildWorkspace ) but I still have a problem in the condition if(node.SyntaxTree.Options.Features.Any()) it's always false and if I removed this condition I get an "invalidoperationexception" Exception that says the Flow-Analysis feature is disabled. please if you have any suggestions or any resources that will help me? – Hamza Feb 09 '19 at 22:02
  • 1
    I have also struggled with this. The important part is to create `CSharpParseOptions options = CSharpParseOptions.Default .WithFeatures(new[] { new KeyValuePair("flow-analysis", "") });` and then pass them to the project. I was digging around the Roslyn code on GitHub and in the library itself using ILSpy tool. I googled around and maybe this can help you https://stackoverflow.com/questions/32769630/how-to-compile-a-c-sharp-file-with-roslyn-programmatically – Karel Frajták Feb 10 '19 at 14:16
2

Depending on the Karel's answer and comment this is how to create a Control Flow Graph without errors:

var source = @"
class C
{
    int M(int x)
    {
      x = 0;
      int y = x * 3;
      return y;
    }
}";

        CSharpParseOptions options = CSharpParseOptions.Default
       .WithFeatures(new[] { new KeyValuePair<string, string>("flow-analysis", "")});

        var tree = CSharpSyntaxTree.ParseText(source, options);
        var compilation = CSharpCompilation.Create("c", new[] { tree });
        var model = compilation.GetSemanticModel(tree, ignoreAccessibility: true);

        var methodBodySyntax = tree.GetCompilationUnitRoot().DescendantNodes().OfType<BaseMethodDeclarationSyntax>().Last();

        var cfgFromSyntax = ControlFlowGraph.Create(methodBodySyntax, model);
Hamza
  • 440
  • 7
  • 20