2017-09-04 12 views
2

TLDR;

私のVisual Studioソリューションで、インデックスプロパティMicrosoft.Extensions.Localization.IStringLocalizer.Item[String]の参照のconst文字列パラメータをすべて検索するにはどうすればよいですか?すべてのソースコードはC#で書かれています。このソリューションは、MVCカミソリのビューもサポートする必要があります。Roslynを使用してVisual Studioソリューション内のすべての参照を検索します。

追加情報

私はRoslynがこの質問に対する答えだと信じています。しかし、私はまだこれを達成するためにAPIを通して自分の道を見つけていません。私は、構文木、コンパイル、またはセマンティックモデルのどちらを使うべきかについても不明です。以下は他のQに基づいた試みです&ここでstackoverflowで。好きなことがあれば、この必要性の理由を読むことができますhere

namespace AspNetCoreLocalizationKeysExtractor 
{ 
    using System; 
    using System.Linq; 
    using Microsoft.CodeAnalysis.FindSymbols; 
    using Microsoft.CodeAnalysis.MSBuild; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      string solutionPath = @"..\source\MySolution.sln"; 
      var msWorkspace = MSBuildWorkspace.Create(); 

      var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result; 

      foreach (var project in solution.Projects.Where(p => p.AssemblyName.StartsWith("MyCompanyNamespace."))) 
      { 
       var compilation = project.GetCompilationAsync().Result; 
       var interfaceType = compilation.GetTypeByMetadataName("Microsoft.Extensions.Localization.IStringLocalizer"); 

       // TODO: Find the indexer based on the name ("Item"/"this"?) and/or on the parameter and return type 
       var indexer = interfaceType.GetMembers().First(); 

       var indexReferences = SymbolFinder.FindReferencesAsync(indexer, solution).Result.ToList(); 

       foreach (var symbol in indexReferences) 
       { 
        // TODO: How to get output comprised by "a location" like e.g. a namespace qualified name and the parameter of the index call. E.g: 
        // 
        // MyCompanyNamespace.MyLib.SomeClass: "Please try again" 
        // MyCompanyNamespace.MyWebApp.Views.Shared._Layout: "Welcome to our cool website" 
        Console.WriteLine(symbol.Definition.ToDisplayString()); 
       } 
      } 
     } 
    } 
} 

更新:回避策

私は簡単な回避策に頼ることにしました@Oxoronから大きな助けにもかかわらず。現在、RoslynはSymbolFinder.FindReferencesAsyncを使って参照を見つけることができません。これは "サイレント" msbuildの失敗によると思われます。これらのエラーは、このように使用できます。

msWorkspace.WorkspaceFailed += (sender, eventArgs) => 
{ 
    Console.Error.WriteLine($"{eventArgs.Diagnostic.Kind}: {eventArgs.Diagnostic.Message}"); 
    Console.Error.WriteLine(); 
}; 

var compilation = project.GetCompilationAsync().Result; 
foreach (var diagnostic in compilation.GetDiagnostics()) 
    Console.Error.WriteLine(diagnostic); 

私の回避策は、おおよそ次のようにされています。もちろん

public void ParseSource() 
{ 
    var sourceFiles = from f in Directory.GetFiles(SourceDir, "*.cs*", SearchOption.AllDirectories) 
        where f.EndsWith(".cs") || f.EndsWith(".cshtml") 
        where !f.Contains(@"\obj\") && !f.Contains(@"\packages\") 
        select f; 
    // _["Hello, World!"] 
    // _[@"Hello, World!"] 
    // _localizer["Hello, World!"] 
    var regex = new Regex(@"_(localizer)?\[""(.*?)""\]"); 
    foreach (var sourceFile in sourceFiles) 
    { 
    foreach (var line in File.ReadLines(sourceFile)) 
    { 
     var matches = regex.Matches(line); 
     foreach (Match match in matches) 
     { 
     var resourceKey = GetResourceKeyFromFileName(sourceFile); 
     var key = match.Groups[2].Value; 
     Console.WriteLine($"{resourceKey}: {key}"); 
     } 
    } 
    } 
} 

ソリューションは防弾ではなく、命名規則に依存しています複数の逐語的な文字列は扱えません。しかし、それはおそらく私たちのために仕事をします:-)

+0

Roslyn Analysysの最初のルール:自動的に公開したいコードを書いてください。 localizer ["anyParam"]のようなコードを探したいですか? – Oxoron

+0

はい、まさに@Oxoron :-)そして、const文字列を使った呼び出し以外の特別な場合を処理するコード解析は必要ありません。 –

答えて

1

thisthisの質問を見ると、インデクサーに役立ちます。

名前空間を特定します。これはもう少し難しいです。 あなただけindexerInvokationの両親を見つけるその後

int spanStart = symbol.Locations[0].Location.SourceSpan.Start; 
Document doc = symbol.Locations[0].Location.Document; 
var indexerInvokation = doc.GetSyntaxRootAsync().Result.DescendantNodes() 
    .FirstOrDefault(node => node.GetLocation().SourceSpan.Start == spanStart); 

のようなコードを使用して、それを決定することができますノードMethodDeclarationSyntax、ClassDeclarationSyntaxなど

UPD1まで。 テストプロジェクトコード:

namespace TestApp 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int test0 = new A().GetInt(); 
      int test1 = new IndexedUno()[2]; 
      int test2 = new IndexedDo()[2]; 
     } 
    } 

    public interface IIndexed 
    { 
     int this[int i] { get; } 
    } 


    public class IndexedUno : IIndexed 
    { 
     public int this[int i] => i; 
    } 

    public class IndexedDo : IIndexed 
    { 
     public int this[int i] => i; 
    } 

    public class A 
    { 
     public int GetInt() { return new IndexedUno()[1]; } 
    } 

    public class B 
    { 
     public int GetInt() { return new IndexedDo()[4]; } 
    } 
} 

検索コード:

using System; 
using System.Linq; 
using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.CSharp.Syntax; 
using Microsoft.CodeAnalysis.FindSymbols; 
using Microsoft.CodeAnalysis.MSBuild; 

namespace AnalyzeIndexers 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string solutionPath = @"PathToSolution.sln"; 
      var msWorkspace = MSBuildWorkspace.Create(); 
      var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result; 

      foreach (var project in solution.Projects.Where(p => p.AssemblyName.StartsWith("TestApp"))) 
      { 
       var compilation = project.GetCompilationAsync().Result; 
       var interfaceType = compilation.GetTypeByMetadataName("TestApp.IIndexed"); 
       var indexer = interfaceType 
        .GetMembers() 
        .OfType<IPropertySymbol>() 
        .First(member => member.IsIndexer); 

       var indexReferences = SymbolFinder.FindReferencesAsync(indexer, solution).Result.ToList(); 

       foreach (var indexReference in indexReferences) 
       { 
        foreach (ReferenceLocation indexReferenceLocation in indexReference.Locations) 
        { 
         int spanStart = indexReferenceLocation.Location.SourceSpan.Start; 
         var doc = indexReferenceLocation.Document; 

         var indexerInvokation = doc.GetSyntaxRootAsync().Result 
          .DescendantNodes() 
          .FirstOrDefault(node => node.GetLocation().SourceSpan.Start == spanStart); 

         var className = indexerInvokation.Ancestors() 
          .OfType<ClassDeclarationSyntax>() 
          .FirstOrDefault() 
          ?.Identifier.Text ?? String.Empty; 

         var @namespace = indexerInvokation.Ancestors() 
          .OfType<NamespaceDeclarationSyntax>() 
          .FirstOrDefault() 
          ?.Name.ToString() ?? String.Empty; 


         Console.WriteLine($"{@namespace}.{className} : {indexerInvokation.GetText()}"); 
        } 
       } 
      } 

      Console.WriteLine(); 
      Console.ReadKey(); 

     } 
    } 
} 

するvarインデクサ= ...コードを見てください - それはタイプからインデクサを抽出します。おそらく、あなたはgetter \ setterで作業する必要があります。

他のポイント:indexerInvokation計算。 SyntaxRootはあまりにも頻繁に使用されますが、多少のキャッシュが必要になるかもしれません。

次へ:クラスと名前空間の検索。メソッドを見つけられませんでしたが、見つからないことをお勧めします:プロパティ、その他のインデクサ、匿名メソッドがインデクサを使用する可能性があります。これに本当に気をつけなければ、MethodDeclarationSyntax型の祖先を見つけてください。

+0

@Oxoronありがとうございます。私は2つのリンクが助けてくれるとは思わない。私はインデクサー呼び出しのシンボルを得ることに本当に苦労しています。 OfTypeジェネリックパラメータのために何をすべきか知っていますか?それとも私はそれを完全に違うのですか? (); –

+0

Thxもう一度@Oxoron。あなたのアップデートの例はまさに私が取り組む必要があったようです。しかし、私はこの作業に多くの時間を費やしてきましたが、私は現在、私の解決策で多くのエラーに悩まされています。 –

+0

このエラーは、Roslynが使用するVSとMSBuildを使用してソリューションを構築する方法の違いに関連しているようです。悲しいかな、私は一種の仕事をすることに決めました(私の質問への私の更新を見てください)。 もう一度お返事いただきありがとうございます。 –

関連する問題