Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a list of referenced Types within a project with Roslyn

Tags:

c#

roslyn

I want to get a list of all used types in a project, for example:

var x = 1;
var y = x.ToString().GetType();

The code should return System.Int32, System.String, System.Type.

What I have is freaking slow... for each file (syntax tree), I do the following:

var root = await syntaxTree.GetRootAsync();
var nodes = root.DescendantNodes(n => true);

if (nodes != null)
{
    var syntaxNodes = nodes as SyntaxNode[] ?? nodes.ToArray();

    // IdentifierNameSyntax:
    //  - var keyword
    //  - identifiers of any kind (including type names)
    var namedTypes = syntaxNodes
        .OfType<IdentifierNameSyntax>()
        .Select(id => this.compilation
                  .GetSemanticModel(id.SyntaxTree)
                  .GetSymbolInfo(id)
                  .Symbol)
        .OfType<INamedTypeSymbol>()
        .ToArray();

    // add the found types to the list
    this.AddRange(namedTypes);

    // ExpressionSyntax:
    //  - method calls
    //  - property uses
    //  - field uses
    //  - all kinds of composite expressions
    var expressionSyntaxs = syntaxNodes
        .OfType<ExpressionSyntax>()
        .ToList();

    var typeSymbols = new List<ITypeSymbol>();
    for (int index = 0; index < expressionSyntaxs.Count; index++)
    {
        ExpressionSyntax ma = expressionSyntaxs[index];
        typeSymbols.Add(this.compilation
                    .GetSemanticModel(ma.SyntaxTree)
                    .GetTypeInfo(ma)
                    .Type);
    }

    var expressionTypes = typeSymbols
        .OfType<INamedTypeSymbol>()
        .ToArray();

    // add the found types to the list
    this.AddRange(expressionTypes);
}

Motivation:

I am making a tool that analyses a project, and tells what .Net framework versions support the given project (e.g. Portable .Net Frameworks).

I hoped it would be faster to match a set of used types with a set of available types from the framework, before compiling.

With small files this code is fast enough, and the total amount of time is less than that of compiling with every possible framework... but with a large files it is unacceptable.

Is there a way to get the list of types in a way that is acceptable?

like image 488
Miguel Angelo Avatar asked Mar 20 '15 22:03

Miguel Angelo


Video Answer


1 Answers

So, I've learnt a lesson: never assume that an immutable structure is going to cache any of it's calculations just because two calls would return the same result.

It is true that when the method is pure, the return is always the same structurally, but it doesn't have to be the same instance.

My assumption lead me to an error: thinking that this.compilation.GetSemanticModel(id.SyntaxTree) would always return the same instance of the semantic model for a given SyntaxTree... it's not true.

I changed my code to the following, and now it is blazing fast:

var root = await syntaxTree.GetRootAsync();
var nodes = root.DescendantNodes(n => true);

var st = root.SyntaxTree;
var sm = this.compilation.GetSemanticModel(st);

if (nodes != null)
{
    var syntaxNodes = nodes as SyntaxNode[] ?? nodes.ToArray();

    // IdentifierNameSyntax:
    //  - var keyword
    //  - identifiers of any kind (including type names)
    var namedTypes = syntaxNodes
        .OfType<IdentifierNameSyntax>()
        .Select(id => sm.GetSymbolInfo(id).Symbol)
        .OfType<INamedTypeSymbol>()
        .ToArray();

    this.Add(namedTypes);

    // ExpressionSyntax:
    //  - method calls
    //  - property uses
    //  - field uses
    //  - all kinds of composite expressions
    var expressionTypes = syntaxNodes
        .OfType<ExpressionSyntax>()
        .Select(ma => sm.GetTypeInfo(ma).Type)
        .OfType<INamedTypeSymbol>()
        .ToArray();

    this.Add(expressionTypes);
}
like image 101
Miguel Angelo Avatar answered Oct 13 '22 19:10

Miguel Angelo