📄 core/tests/CallGraphBuilderTests/SourceToCallGraphHelper.cs
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using Shouldly;

namespace Cfa.Tests.CallGraphBuilderTests;

internal static class SourceToCallGraphHelper
{
    public static async Task<(ISymbol, IImmutableDictionary<ISymbol, IImmutableList<Call>>)> FindCallees(
        this string source,
        MessageOptions? messages = null
    )
    {
        var (start, callGraphBuilder) = await source.GetStartSymbolAndCallGraphBuilder(messages);
        await callGraphBuilder.FindCallees(start, CancellationToken.None);
        return (start, callGraphBuilder.ReachableFromSymbols.ToImmutable());
    }

    public static async Task<(ISymbol, IImmutableDictionary<ISymbol, IImmutableList<Call>>)> FindCallers(
        this string source
    )
    {
        var (start, callGraphBuilder) = await source.GetStartSymbolAndCallGraphBuilder();
        await callGraphBuilder.FindCallers(start, CancellationToken.None);
        return (start, callGraphBuilder.ReachesToSymbols.ToImmutable());
    }

    private static async Task<(ISymbol, CallGraphBuilder)> GetStartSymbolAndCallGraphBuilder(
        this string source,
        MessageOptions? messages = null
    )
    {
        var workspace = new AdhocWorkspace();
        var projectInfo = ProjectInfo.Create(
            ProjectId.CreateNewId(),
            VersionStamp.Default,
            name: "Example",
            assemblyName: "Example",
            language: LanguageNames.CSharp,
            compilationOptions: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
            metadataReferences: [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)]
        );
        workspace
            .TryApplyChanges(
                workspace
                    .AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Default))
                    .AddProject(projectInfo)
            )
            .ShouldBeTrue();
        workspace.AddDocument(projectInfo.Id, "Example.cs", SourceText.From(source));
        var project = workspace.CurrentSolution.GetProject(projectInfo.Id).ShouldNotBeNull();
        var compilation = (await project.GetCompilationAsync()).ShouldNotBeNull();
        compilation.GetDiagnostics().ShouldBeEmpty();
        var start = compilation
            .GetTypeByMetadataName("Example")
            .ShouldNotBeNull()
            .GetMembers("Start")
            .ShouldHaveSingleItem();
        var callGraphBuilder = new CallGraphBuilder(
            new ScannerOptions
            {
                Messages = messages ?? (source.Contains(MessageSource.Contracts) ? MessageSource.Options : new()),
            },
            workspace.CurrentSolution,
            [compilation]
        );
        return (start, callGraphBuilder);
    }
}