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
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
A code query is generated here is the result, then click export to graph:
Et voilà! You can easily change the grouping, here it is grouped by parent class:
See the documentation here.