Using Roslyn to find interfaces within a solution The intent of the below code is to find all interfaces within a solution whose fully-qualified names match the given predicate. It seems to work, but as I am new to Roslyn I'm guessing there are things that could be improved. I'd appreciate any pointers.

public async Task&lt;IImmutableList&lt;INamedTypeSymbol&gt;&gt; GetMatchingInterfacesInSolution(string solutionPath, Func&lt;string, bool&gt; predicate)
{
    var workspace = MSBuildWorkspace.Create();
    var solution = await workspace.OpenSolutionAsync(solutionPath);
    var compilations = await Task.WhenAll(solution.Projects.Select(x =&gt; x.GetCompilationAsync()));

    return compilations
        .SelectMany(x =&gt; x.SyntaxTrees.Select(y =&gt; new { Compilation = x, SyntaxTree = y }))
        .Select(x =&gt; x.Compilation.GetSemanticModel(x.SyntaxTree))
        .SelectMany(
            x =&gt; x
                .SyntaxTree
                .GetRoot()
                .DescendantNodes()
                .OfType&lt;InterfaceDeclarationSyntax&gt;()
                .Select(y =&gt; x.GetDeclaredSymbol(y)))
        .Where(x =&gt; predicate(x.ToDisplayString()))
        .ToImmutableList();
}


Here's another approach I tried. I couldn't really see any benefit to doing it this way. It's more verbose and difficult to follow:

public static async Task&lt;IImmutableList&lt;INamedTypeSymbol&gt;&gt; GetMatchingInterfacesInSolution(string solutionPath, Func&lt;string, bool&gt; predicate)
{
    var workspace = MSBuildWorkspace.Create();
    var solution = await workspace.OpenSolutionAsync(solutionPath);
    var compilations = await Task.WhenAll(solution.Projects.Select(x =&gt; x.GetCompilationAsync()));

    return compilations
        .Select(x =&gt; x.Assembly.GlobalNamespace)
        .SelectMany(x =&gt; FindInterfacesRecursive(x, predicate))
        .ToImmutableList();
}

private static IEnumerable&lt;INamedTypeSymbol&gt; FindInterfacesRecursive(INamespaceSymbol @namespace, Func&lt;string, bool&gt; predicate)
{
    foreach (var member in @namespace.GetMembers())
    {
        var childNamespace = member as INamespaceSymbol;

        if (childNamespace != null)
        {
            foreach (var @interface in FindInterfacesRecursive(childNamespace, predicate))
            {
                yield return @interface;
            }

            continue;
        }

        var namedType = member as INamedTypeSymbol;

        if (namedType == null || namedType.TypeKind != TypeKind.Interface)
        {
            continue;
        }

        if (predicate == null || predicate(namedType.ToDisplayString()))
        {
            yield return namedType;
        }
    }
}
Name captured variables as you would name local variables.
First SelectMany and Select can be merged into a single SelectMany.


code: 

public async Task&lt;IImmutableList&lt;ISymbol&gt;&gt; GetMatchingInterfacesInSolution(string solutionPath, Func&lt;string, bool&gt; predicate)
{
    var workspace = MSBuildWorkspace.Create();
    var solution = await workspace.OpenSolutionAsync(solutionPath);
    var compilations = await Task.WhenAll(solution.Projects.Select(x =&gt; x.GetCompilationAsync()));

    return compilations
        .SelectMany(compilation =&gt; compilation.SyntaxTrees.Select(syntaxTree =&gt; compilation.GetSemanticModel(syntaxTree)))
        .SelectMany(
            semanticModel =&gt; semanticModel
                .SyntaxTree
                .GetRoot()
                .DescendantNodes()
                .OfType&lt;InterfaceDeclarationSyntax&gt;()
                .Select(interfaceDeclarationSyntax =&gt; semanticModel.GetDeclaredSymbol(interfaceDeclarationSyntax)))
        .Where(symbol =&gt; predicate(symbol.ToDisplayString()))
        .ToImmutableList();
}