C# 如何使用Roslyn通过扩展方法、静态类中的方法和带有ref/out参数的方法访问调用

C# 如何使用Roslyn通过扩展方法、静态类中的方法和带有ref/out参数的方法访问调用,c#,.net,roslyn,C#,.net,Roslyn,我正在创建一个开源项目,用于创建.NETUML序列图,该序列图利用一个名为js序列图的javascript库。我不确定Roslyn是否适合这项工作,但我想我会试一试,所以我准备了一些概念验证代码,尝试获取所有方法及其调用,然后以js序列图可以解释的形式输出这些调用 代码生成了一些输出,但并没有捕获所有内容。我似乎无法通过扩展方法捕获调用,静态类中静态方法的调用 我确实看到使用out参数调用方法,但没有任何形式扩展BaseMethodDeclarationSyntax 这是代码(请记住,这是概念验

我正在创建一个开源项目,用于创建.NETUML序列图,该序列图利用一个名为js序列图的javascript库。我不确定Roslyn是否适合这项工作,但我想我会试一试,所以我准备了一些概念验证代码,尝试获取所有方法及其调用,然后以js序列图可以解释的形式输出这些调用

代码生成了一些输出,但并没有捕获所有内容。我似乎无法通过扩展方法捕获调用,静态类中静态方法的调用

我确实看到使用
out
参数调用方法,但没有任何形式扩展
BaseMethodDeclarationSyntax

这是代码(请记住,这是概念验证代码,因此我没有完全遵循最佳实践,但我不要求在此进行代码审查……此外,我习惯于使用任务,因此我在等待中无所事事,但不完全确定我是否正确使用了它)

使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Linq;
使用System.Reflection.Emit;
使用System.Threading.Tasks;
使用Microsoft.CodeAnalysis;
使用Microsoft.CodeAnalysis.CSharp;
使用Microsoft.CodeAnalysis.CSharp.Syntax;
使用Microsoft.CodeAnalysis.Formatting;
使用Microsoft.CodeAnalysis.MSBuild;
使用Microsoft.CodeAnalysis.FindSymbols;
使用System.Collections.Immutable;
名称空间图
{
班级计划
{
静态void Main(字符串[]参数)
{
字符串solutionName=“Diagrams”;
字符串solutionExtension=“.sln”;
字符串solutionFileName=solutionName+solutionExtension;
字符串rootPath=@“C:\Workspace\”;
字符串solutionPath=rootPath+solutionName+@“\”+solutionFileName;
MSBuildWorkspace=MSBuildWorkspace.Create();
DiagramGenerator DiagramGenerator=新的DiagramGenerator(解决方案路径、工作空间);
diagramGenerator.ProcessSolution();
#区域参考
//TODO:ReferencedSymbol.Location是访问MethodDeclarationSyntax的更好方法吗?
//INamedTypeSymbol programClass=compilation.GetTypeByMetadataName(“DotNetDiagrams.Program”);
//IMethodSymbol barMethod=programClass.GetMembers(“Bar”)。首先(s=>s.Kind==SymbolKind.Method)作为IMethodSymbol;
//IMethodSymbol fooMethod=programClass.GetMembers(“Foo”)。首先(s=>s.Kind==SymbolKind.Method)作为IMethodSymbol;
//ITypeSymbol fooSymbol=fooMethod.ContainingType;
//ITypeSymbol BARSSYMBOL=barMethod.ContainingType;
//Assert(barMethod!=null);
//Assert(fooMethod!=null);
//List barReferencedSymbols=SymbolFinder.FindReferencesAsync(barMethod,solution.Result.ToList();
//List fooReferencedSymbols=SymbolFinder.FindReferencesAsync(fooMethod,solution.Result.ToList();
//Assert(barReferencedSymbols.First().Locations.Count()==1);
//Assert(fooReferencedSymbols.First().Locations.Count()=0);
#端区
Console.ReadKey();
}
}
类图生成器
{
私人解决方案(u解决方案),;
公共DiagramGenerator(字符串解决方案路径,MSBuildWorkspace工作区)
{
_解决方案=workspace.OpenSolutionAsync(solutionPath).Result;
}
公共异步void ProcessSolution()
{
foreach(解决方案项目中的项目)
{
Compilation Compilation=await project.GetCompilationAsync();
过程编译(编译);
}
}
私有异步void进程编译(编译)
{
var trees=compilation.SyntaxTrees;
foreach(树中的var树)
{
var root=await tree.GetRootAsync();
var classes=root.degenantNodes().OfType();
foreach(类中的var@class)
{
ProcessClass(@class,编译,树,根);
}
}
}
私有void进程类(
ClassDeclarationSyntax@class
,汇编
,综合分类树
,SyntaxNode根)
{
var方法=@class.degenantNodes().OfType();
foreach(方法中的var方法)
{
var model=compilation.GetSemanticModel(树);
//获取方法对应的MethodSymbol
var methodSymbol=model.GetDeclaredSymbol(方法);
//获取上述代码中的所有InvocationExpressionSyntax。
var allInvocations=root.genderantnodes().OfType();
//使用GetSymbolInfo()查找目标方法的调用
职业匹配=
其中(i=>model.GetSymbolInfo(i).Symbol.Equals(methodSymbol));
ProcessMethod(匹配职业、方法、@class);
}
var delegates=@class.degenantnodes().OfType();
foreach(代理中的var@delegate)
{
var model=compilation.GetSemanticModel(树);
//获取方法对应的MethodSymbol
var methodSymbol=model.GetDeclaredSymbol(@delegate);
//获取上述代码中的所有InvocationExpressionSyntax。
var allInvocations=tree.GetRoot().genderantnodes().OfType();
//使用GetSymbolInfo()查找目标方法的调用
职业匹配=
所有地点,在哪里
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.FindSymbols;
using System.Collections.Immutable;

namespace Diagrams
{
    class Program
    {
        static void Main(string[] args)
        {
            string solutionName = "Diagrams";
            string solutionExtension = ".sln";
            string solutionFileName = solutionName + solutionExtension;
            string rootPath = @"C:\Workspace\";
            string solutionPath = rootPath + solutionName + @"\" + solutionFileName;

            MSBuildWorkspace workspace = MSBuildWorkspace.Create();
            DiagramGenerator diagramGenerator = new DiagramGenerator( solutionPath, workspace );
            diagramGenerator.ProcessSolution();

            #region reference

            //TODO: would ReferencedSymbol.Locations be a better way of accessing MethodDeclarationSyntaxes? 
            //INamedTypeSymbol programClass = compilation.GetTypeByMetadataName("DotNetDiagrams.Program");

            //IMethodSymbol barMethod = programClass.GetMembers("Bar").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol;
            //IMethodSymbol fooMethod = programClass.GetMembers("Foo").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol;

            //ITypeSymbol fooSymbol = fooMethod.ContainingType;
            //ITypeSymbol barSymbol = barMethod.ContainingType;

            //Debug.Assert(barMethod != null);
            //Debug.Assert(fooMethod != null);

            //List<ReferencedSymbol> barReferencedSymbols = SymbolFinder.FindReferencesAsync(barMethod, solution).Result.ToList();
            //List<ReferencedSymbol> fooReferencedSymbols = SymbolFinder.FindReferencesAsync(fooMethod, solution).Result.ToList();

            //Debug.Assert(barReferencedSymbols.First().Locations.Count() == 1);
            //Debug.Assert(fooReferencedSymbols.First().Locations.Count() == 0);

            #endregion

            Console.ReadKey();
        }
    }

    class DiagramGenerator
    {
        private Solution _solution;

        public DiagramGenerator( string solutionPath, MSBuildWorkspace workspace )
        {
            _solution = workspace.OpenSolutionAsync(solutionPath).Result;
        }

        public async void ProcessSolution()
        {
            foreach (Project project in _solution.Projects)
            {
                Compilation compilation = await project.GetCompilationAsync();
                ProcessCompilation(compilation);
            }
        }

        private async void ProcessCompilation(Compilation compilation)
        {
            var trees = compilation.SyntaxTrees;

            foreach (var tree in trees)
            {
                var root = await tree.GetRootAsync();
                var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();

                foreach (var @class in classes)
                {
                    ProcessClass( @class, compilation, tree, root );
                }
            }
        }

        private void ProcessClass(
              ClassDeclarationSyntax @class
            , Compilation compilation
            , SyntaxTree tree
            , SyntaxNode root)
        {
            var methods = @class.DescendantNodes().OfType<MethodDeclarationSyntax>();

            foreach (var method in methods)
            {
                var model = compilation.GetSemanticModel(tree);
                // Get MethodSymbol corresponding to method
                var methodSymbol = model.GetDeclaredSymbol(method);
                // Get all InvocationExpressionSyntax in the above code.
                var allInvocations = root.DescendantNodes().OfType<InvocationExpressionSyntax>();
                // Use GetSymbolInfo() to find invocations of target method
                var matchingInvocations =
                    allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol));

                ProcessMethod( matchingInvocations, method, @class);
            }

            var delegates = @class.DescendantNodes().OfType<DelegateDeclarationSyntax>();

            foreach (var @delegate in delegates)
            {
                var model = compilation.GetSemanticModel(tree);
                // Get MethodSymbol corresponding to method
                var methodSymbol = model.GetDeclaredSymbol(@delegate);
                // Get all InvocationExpressionSyntax in the above code.
                var allInvocations = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>();
                // Use GetSymbolInfo() to find invocations of target method
                var matchingInvocations =
                    allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol));

                ProcessDelegates(matchingInvocations, @delegate, @class);
            }

        }

        private void ProcessMethod(
              IEnumerable<InvocationExpressionSyntax> matchingInvocations
            , MethodDeclarationSyntax methodDeclarationSyntax
            , ClassDeclarationSyntax classDeclarationSyntax )
        {
            foreach (var invocation in matchingInvocations)
            {
                MethodDeclarationSyntax actingMethodDeclarationSyntax = null;
                if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax))
                {
                    var r = methodDeclarationSyntax;
                    var m = actingMethodDeclarationSyntax;

                    PrintCallerInfo(
                        invocation
                        , classDeclarationSyntax
                        , m.Identifier.ToFullString()
                        , r.ReturnType.ToFullString()
                        , r.Identifier.ToFullString()
                        , r.ParameterList.ToFullString()
                        , r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty
                        );
                }
            }
        }

        private void ProcessDelegates( 
              IEnumerable<InvocationExpressionSyntax> matchingInvocations
            , DelegateDeclarationSyntax delegateDeclarationSyntax
            , ClassDeclarationSyntax classDeclarationSyntax )
        {
            foreach (var invocation in matchingInvocations)
            {
                DelegateDeclarationSyntax actingMethodDeclarationSyntax = null;

                if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax))
                {
                    var r = delegateDeclarationSyntax;
                    var m = actingMethodDeclarationSyntax;

                    PrintCallerInfo(
                        invocation
                        , classDeclarationSyntax
                        , m.Identifier.ToFullString()
                        , r.ReturnType.ToFullString()
                        , r.Identifier.ToFullString()
                        , r.ParameterList.ToFullString()
                        , r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty
                    );
                }
            }
        }

        private void PrintCallerInfo(
              InvocationExpressionSyntax invocation
            , ClassDeclarationSyntax classBeingCalled
            , string callingMethodName
            , string returnType
            , string calledMethodName
            , string calledMethodArguments
            , string calledMethodTypeParameters = null )
        {
            ClassDeclarationSyntax parentClassDeclarationSyntax = null;
            if (!SyntaxNodeHelper.TryGetParentSyntax(invocation, out parentClassDeclarationSyntax))
            {
                throw new Exception();
            }

            calledMethodTypeParameters = calledMethodTypeParameters ?? String.Empty;

            var actedUpon = classBeingCalled.Identifier.ValueText;
            var actor = parentClassDeclarationSyntax.Identifier.ValueText;
            var callInfo = callingMethodName + "=>" + calledMethodName + calledMethodTypeParameters + calledMethodArguments;
            var returnCallInfo = returnType;

            string info = BuildCallInfo(
                  actor
                , actedUpon
                , callInfo
                , returnCallInfo);

            Console.Write(info);
        }

        private string BuildCallInfo(string actor, string actedUpon, string callInfo, string returnInfo)
        {
            const string calls = "->";
            const string returns = "-->";
            const string descriptionSeparator = ": ";

            string callingInfo = actor + calls + actedUpon + descriptionSeparator + callInfo;
            string returningInfo = actedUpon + returns + actor + descriptionSeparator + "returns " + returnInfo;

            callingInfo = callingInfo.RemoveNewLines(true);
            returningInfo = returningInfo.RemoveNewLines(true);

            string result = callingInfo + Environment.NewLine;
            result += returningInfo + Environment.NewLine;

            return result;
        }
    }

    static class SyntaxNodeHelper
    {
        public static bool TryGetParentSyntax<T>(SyntaxNode syntaxNode, out T result) 
            where T : SyntaxNode
        {
            // set defaults
            result = null;

            if (syntaxNode == null)
            {
                return false;
            }

            try
            {
                syntaxNode = syntaxNode.Parent;

                if (syntaxNode == null)
                {
                    return false;
                }

                if (syntaxNode.GetType() == typeof (T))
                {
                    result = syntaxNode as T;
                    return true;
                }

                return TryGetParentSyntax<T>(syntaxNode, out result);
            }
            catch
            {
                return false;
            }
        }
    }

    public static class StringEx
    {
        public static string RemoveNewLines(this string stringWithNewLines, bool cleanWhitespace = false)
        {
            string stringWithoutNewLines = null;
            List<char> splitElementList = Environment.NewLine.ToCharArray().ToList();

            if (cleanWhitespace)
            {
                splitElementList.AddRange(" ".ToCharArray().ToList());
            }

            char[] splitElements = splitElementList.ToArray();

            var stringElements = stringWithNewLines.Split(splitElements, StringSplitOptions.RemoveEmptyEntries);
            if (stringElements.Any())
            {
                stringWithoutNewLines = stringElements.Aggregate(stringWithoutNewLines, (current, element) => current + (current == null ? element : " " + element));
            }

            return stringWithoutNewLines ?? stringWithNewLines;
        }
    }
}
private async Task<List<MethodDeclarationSyntax>> GetMethodSymbolReferences( IMethodSymbol methodSymbol )
{
    var references = new List<MethodDeclarationSyntax>();

    var referencingSymbols = await SymbolFinder.FindCallersAsync(methodSymbol, _solution);
    var referencingSymbolsList = referencingSymbols as IList<SymbolCallerInfo> ?? referencingSymbols.ToList();

    if (!referencingSymbolsList.Any(s => s.Locations.Any()))
    {
        return references;
    }

    foreach (var referenceSymbol in referencingSymbolsList)
    {
        foreach (var location in referenceSymbol.Locations)
        {
            var position = location.SourceSpan.Start;
            var root = await location.SourceTree.GetRootAsync();
            var nodes = root.FindToken(position).Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>();

            references.AddRange(nodes);
        }
    }

    return references;
}