如何通过扩展方法,静态类中的方法以及使用Roslyn的ref / out参数的方法来访问调用

我正在创建一个用于创建.NET UML Sequence Diagrams的开源项目,该项目利用了一个名为js-sequence-diagrams的javascript库。 我不确定Roslyn是否适合这项工作,但我认为我会试一试,所以我已经整理了一些概念代码certificate,它试图获取所有方法及其调用,然后以一种forms输出这些调用。可以通过js-sequence-diagrams来解释。

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

我确实看到带有out参数的方法调用,但没有扩展BaseMethodDeclarationSyntax任何forms

这是代码(请记住,这是概念代码的certificate,所以我没有完全遵循最佳实践,但我不是在这里请求代码审查…而且,我习惯使用任务所以我在搞乱等待,但我不完全确定我正在使用它)

https://gist.github.com/SoundLogic/11193841

 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 barReferencedSymbols = SymbolFinder.FindReferencesAsync(barMethod, solution).Result.ToList(); //List 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(); 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(); 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(); // 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(); 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(); // 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 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 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(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(syntaxNode, out result); } catch { return false; } } } public static class StringEx { public static string RemoveNewLines(this string stringWithNewLines, bool cleanWhitespace = false) { string stringWithoutNewLines = null; List 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; } } } 

这里的任何指导将非常感谢!

ProcessClass方法中使用methodSymbol我接受了Andy的建议,并提出了下面的内容(尽管我想可能有一个更简单的方法):

 private async Task> GetMethodSymbolReferences( IMethodSymbol methodSymbol ) { var references = new List(); var referencingSymbols = await SymbolFinder.FindCallersAsync(methodSymbol, _solution); var referencingSymbolsList = referencingSymbols as IList ?? 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(); references.AddRange(nodes); } } return references; } 

通过将输出文本插入到js-sequence-diagrams中生成的结果图像(我已经更新了github gist的完整源代码,任何人都觉得它有用 – 我排除了方法参数,所以图表很容易消化,但这些可以选择被重新开启):

编辑:

我已经更新了代码(参见github gist ),所以现在调用按照它们的顺序显示(基于调用方法中的调用方法的跨度起始位置,来自FindCallersAsync的结果):

在此处输入图像描述