0

Is there anyway to connect a piece of code to its "using" statement using the Roslyn framework?

For example, given this piece of code:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Serilog;
using System.Collections.Generic;

namespace CodeAnalyzer.Service.CodeAnalysis.CSharp
{
public static class CSharpCodeAnalyser
{

/// <summary>
/// Retrieves all of the using statements from a code page
/// </summary>
/// <param name="codeDocument"></param>
/// <param name="logger"></param>
/// <returns></returns>
public static List<string> IdentifyUsings(string codeDocument, ILogger logger)
{
    var usingList = new List<string>();

    try
    {
        SyntaxTree tree = CSharpSyntaxTree.ParseText(codeDocument);
        CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
        foreach (var item in root.Usings)
        {
            usingList.Add(item.Name.ToString());
        }
    }
    catch (System.Exception ex)
    {
        logger.Error(ex, ex.Message);
        throw;
    }

    return usingList;
}

} }

Could I know what assembly "CSharpSyntaxTree" belongs to?

Matt
  • 384
  • 2
  • 10
  • 1
    If you had this information, what would you then do with it? I ask because there's some fun cases around extension methods and such you have to think about, and depending on your goal you may need to do different things. – Jason Malinowski Aug 19 '20 at 18:37
  • So essentially I’m trying to create a tool that will analyze commits to a git repo and identify which libraries were used in the code update so I can connect it to a developer. That way I can track a developers skills – Matt Aug 19 '20 at 18:53
  • What exactly do you want? Know the namespace or the assembly of a type? Your previous comment does not match the question. – Paulo Morgado Aug 19 '20 at 19:10
  • Paulo, have a look at the example I posted. I would like to know both the namespace and the assembly. But if I had to choose one it would be the assembly. I figured if I could find out the namespace I could use the code I already have to identify the assembly. But a simple piece of code that could do both at once would be optimal. – Matt Aug 19 '20 at 19:14
  • So do you actually need to connect the types to usings, or is your real question "you just want to know what namespaces the types are from?" For example, does your hypothetical tool consider "Foo" and "Fully.Qualified.Foo" to both be a use of "Fully.Qualified"? – Jason Malinowski Aug 19 '20 at 22:04
  • I just really need to know what namespace and hence assembly the type is from – Matt Aug 19 '20 at 22:27
  • Ah, that's much easier! And if you just care about the assembly then you can skip the namespace entirely since a namespace could have things from multiple assemblies at once. You may want to rephrase you question and then we can answer. – Jason Malinowski Aug 19 '20 at 23:01
  • Ok I changed it – Matt Aug 19 '20 at 23:02

1 Answers1

1

So I figured out how to do this.

First I needed to build a workspace containing my entire solution. I did this through the Buildalyzer tool

        public static Tuple<AdhocWorkspace, IEnumerable<Project>> GetWorkspace(string solutionFilePath, ILogger logger)
    {
        Tuple<AdhocWorkspace, IEnumerable<Project>> results;
        var projectList = new List<Project>();
        AdhocWorkspace workspace = new AdhocWorkspace();


        try
        {
            AnalyzerManager manager = new AnalyzerManager(solutionFilePath);
            foreach (var project in manager.Projects)
            {
                projectList.Add(project.Value.AddToWorkspace(workspace));
            }
            results = new Tuple<AdhocWorkspace, IEnumerable<Project>>(workspace, projectList);
        }
        catch (Exception ex)
        {
            logger.Error(ex, ex.Message);
            throw;
        }
        return results;
    }

Once I had a workspace all I needed to do was the following:

var workspace = GetWorkspace(solutionName, _logger);


                foreach (var project in workspace.Item1.CurrentSolution.Projects)
                {
                    foreach (var document in project.Documents)
                    {
                        _logger.Information("");
                        _logger.Information(project.Name + "\t\t\t" + document.Name);
                        var semanticModel = await document.GetSemanticModelAsync();
                        var root = await document.GetSyntaxRootAsync();

                        foreach (var item in root.DescendantNodes())
                        {
                            var typeInfo = semanticModel.GetTypeInfo(item);
                            if (typeInfo.Type != null)
                            {
                                _logger.Information(String.Format("Node: {0}", item.ToString()));
                                _logger.Information(String.Format("Type:{0}", typeInfo.Type.Name.ToString()));
                                _logger.Information(String.Format("ContainingAssembly:{0}", typeInfo.Type.ContainingAssembly));
                                _logger.Information(String.Format("ContainingNamespace:{0}", typeInfo.Type.ContainingNamespace));
                            }

                        }

                        
                    }
                }
Matt
  • 384
  • 2
  • 10