generate incoming call graph

111 Views Asked by At

I'm trying to pretty print the method calls until a specific method

for example let's take "DisplayHelp" from this simple random repository

I expect for something like

Shell.ctor -> GetHelp -> DisplayHelp

using Broslyn; // dotnet add package Broslyn --version 1.2.0
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;

var csproj = "./clisharp/clisharp.csproj"; // git clone https://github.com/citrus-thunder/clisharp
var methodName = "DisplayHelp";

var workspace = CSharpCompilationCapture.Build(csproj).Workspace;
var project = workspace.CurrentSolution.Projects.FirstOrDefault();
var compilation = await project.GetCompilationAsync();

var targetMethod = compilation.SyntaxTrees
    .SelectMany(tree => tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>())
    .FirstOrDefault(method => method.Identifier.ValueText == methodName);

var incomingCallGraph = new Dictionary<IMethodSymbol, List<IMethodSymbol>>();
BuildIncomingCallGraph(compilation, targetMethod, incomingCallGraph);


void BuildIncomingCallGraph(Compilation compilation, MethodDeclarationSyntax targetMethodSyntax, Dictionary<IMethodSymbol, List<IMethodSymbol>> incomingCallGraph) {
    var targetMethodSymbol = compilation.GetSemanticModel(targetMethodSyntax.SyntaxTree).GetDeclaredSymbol(targetMethodSyntax) as IMethodSymbol;

    var allMethods = compilation
        .SyntaxTrees
        .SelectMany(tree => tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>())
        .Select(methodSyntax => compilation.GetSemanticModel(methodSyntax.SyntaxTree).GetDeclaredSymbol(methodSyntax) as IMethodSymbol)
        .Where(methodSymbol => methodSymbol != null);

    foreach (var _mtd in allMethods) {
        var invocations = SymbolFinder
            .FindCallersAsync(targetMethodSymbol, workspace.CurrentSolution, cancellationToken: default).Result;
        var callers = invocations
            .Select(invocation => invocation.CallingSymbol as IMethodSymbol)
            .Where(caller => caller != null);
        foreach (var caller in callers) {
            if (!incomingCallGraph.ContainsKey(targetMethodSymbol)) {
                incomingCallGraph[targetMethodSymbol] = new List<IMethodSymbol>();
            }

            incomingCallGraph[targetMethodSymbol].Add(caller);
        }
    }
}

Current output:

Method: DisplayHelp -> Caller: GetHelp -> Caller: DisplayHelp -> Caller: GetHelp -> Caller: DisplayHelp -> Caller: GetHelp -> Caller: DisplayHelp -> Caller: GetHelp -> Caller: DisplayHelp

1

There are 1 best solutions below

1
Patrick from NDepend team On

You can easily generate a call graph with NDepend.

Just download the free trial, starts VisualNDepend.exe, analyze your .NET solution, search for your method in the Search panel, then right click Select methods that are using me directly or indirectly

NDepend search for method

A code query is generated here is the result, then click export to graph:

NDepend code query generated

Et voilà! You can easily change the grouping, here it is grouped by parent class:

NDepend call graph

See the documentation here.