2014-04-23 21 views
5

ile yöntemler/yöntemlerle çağrılar nasıl erişilir js-sequence adlı javascript kütüphanesinden yararlanan .NET UML Sekansı Diyagramları oluşturmak için bir açık kaynak projesi oluşturmaya çalışıyorum -diagrams. Roslyn'in iş için doğru bir araç olduğundan emin değilim, ama bir çekim yapacağımı düşündüm, bu yüzden tüm yöntemleri ve onların çağrışımlarını elde etmeye çalışan bazı kavram kodu kanıtlarını bir araya getirdim ve daha sonra bu çağrışımları çıktılar. js-dizi-diyagramları ile yorumlanabilir.Uzantı yöntemleri, statik sınıflardaki yöntemler ve Roslyn

Kod, bazı çıktılar üretir, ancak her şeyi yakalamaz. Uzantıları, statik sınıflardaki statik yöntemlerin çağrışımları ile çağırmaları yakalayamıyorum.

Ben out parametrelerle yöntemlerin çağırmaları görüyorum, ama burada BaseMethodDeclarationSyntax

uzanan herhangi bir biçimde kodu (bu kavram kod kanıtıdır akılda tutmak ve bu yüzden tamamen iyi-takip etmedi olduğunu uygulamalar, ama burada bir kod inceleme istemeden değilim ... Ayrıca, ben bu yüzden bekliyor ile karıştırmasını am Görevler kullanarak alışkınım ama tamamen emin ben

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

) henüz düzgün kullanıyorum değilim
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; 
     } 
    } 
} 

Burada herhangi bir rehberlik çok takdir edilecektir!

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; 
} 

ve ürettiği çıkan resim: ProcessClass yöntemde methodSymbol kullanma

+2

Anekdotal ben Roslyn iş için doğru aracı olduğunu söyleyebilirim, ama sen bir noktada bile Roslyn derleyici olacak en (eksik olduğunu düşünüyorum bilgilerin bırakmamak amacıyla özel parçaları ile uzatmak gerekebilir uzatma yöntemleri vb. Bu kadar yeni olduğu için de bir iz bırakıyor olabilirsiniz, lütfen rapor verin ve bu soruyu yanıtlayın. Bu dış SO ile bir yere varıncaya kadar :-) :-) –

+2

Bir sözdizimi düğümü yazmayı düşünün. – SLaks

+0

@AdamHouldsworth Ben her zaman cevap ile geri rapor gerekir Ben onunla herhangi bir yere gitmeli :) Ayrıca, bu proje tamamen açık kaynak olacak ve ben P.O.C. geçtikten sonra GitHub bağlantısını paylaşacağız – Jordan

cevap

1

(Ben hayal rağmen daha kolay bir bu konuda gitmek için yol olabilir) Andy'nin öneri aldı ve aşağıda ile geldi

Düzenleme: - js-sequence-diagrams içine çıkış metni takmayı (diyagram kolay sindirmek edildi yüzden yöntem parametreleri hariç, ancak bu isteğe geri açılabilir Ben herkes faydalı buluyorum gerektiğini bunun için tam kaynak ile github gist güncelledik) :

Ben şimdi aramalar (FindCallersAsync gelen sonuçları yoluyla arama yöntemi içinden bir adlandırılan yöntemin yayılma başlangıç ​​konumuna göre) yapıldığı sırayla gösterilir kodunu (bkz github gist) güncelledik:

enter image description here

İlgili konular