2016-08-11 9 views
3

与えられたSyntaxNodeの子孫でどのディレクティブが使用されているかをどのように知ることができますか?どのディレクティブがメソッドで使用されているかを見つける

次の例を参照してください。 https://dotnetfiddle.net/mCzNST ここでは、Class1で使用されている使用法を知りたいが、Class2で使用されている使用法は無視したい。

+0

これは実際には思ったよりもはるかに複雑です。一見無関係な 'using'ディレクティブを削除すると、lambdasの複雑な場合のオーバーロード解決に影響する可能性があります。 – SLaks

+0

私は使い方を削除したくありません。私はちょうどどの使用が確実に使用されているか知りたい。 – TWT

+0

@SLaksのポイントは、過負荷の解決は使用方法の微妙な方法に依存するため、「間違いなく使用」が難しいことです。 「タイプをバインドしてリストを作成する」というヒューリスティックがあなたの使用に十分適しているなら、それは問題ありません。しかし、ヒューリスティックであることに注意してください。 Roslynアーキテクチャーでは、コンパイラは、私たちが提供するAPIを使って実行できないため、使用されていない使用情報を直接生成します。 –

答えて

3

セマンティックモデルを使用して、構文ノードに関連するタイプを取得できます。タイプ情報は、関連する使用法を識別するために使用できる関連する名前空間を認識します。すべてのノードを反復処理するときには、メソッドの戻り値と変数、プロパティ、およびフィールドの型が返されます。あなたは、特定のタイプ(例えばInvocationExpressionSyntax)にノードを制限する場合は、usingsを取得したい場合にのみ、戻り値の型、変数の型など

private static IEnumerable<string> FindUsedUsings(SemanticModel model, SyntaxNode node) 
{ 
    var resultList = new List<string>(); 
    foreach (var identifier in node.DescendantNodesAndSelf()) 
    { 
     var typeInfo = model.GetTypeInfo(identifier); 
     if (typeInfo.Type != null) 
     { 
      resultList.Add(typeInfo.Type.ContainingNamespace.ToDisplayString()); 
     } 
    } 
    return resultList.Distinct().ToList(); 
} 

は、しかし、あなたはすべての宣言タイプを識別する必要があります。

using System; 
using Text = System.String; 
using static System.Console; 

異なるロジックで使用しているすべてのタイプを識別することができます。あなたが考慮すべき使用しての最初のタイプの場合

  • varが一切使用して
  • PredefinedTypes(文字列、intは)全く使用して
  • ダイナミックは一切使用して
  • QualifiedTypeNamesは一切使用を必要としない必要はありません必要はありませんが必要です

第2の種類の使用については、次の点を考慮する必要があります。

  • エイリアスを使用してタイプを解決できますが、元の名前を使用することもできます。上の例を使用すると、Statement文字列sample = Text.Emptyを書くことができます。
  • タイプの識別子がないため、Expression Statementを使用して呼び出しを見つける必要があります。 以下のソリューションでは、メソッドのIdentifierNameSyntaxにTypeSymbolが指定されていないため、静的なMyNamespace.Classnameを使用すると正しく解決されないため、型を解決できませんでした。

    これは、2つの問題が解決することができますタイプは使用して無効を解決した場合は型がクラスMyNamespace.Class2

  • を分析する際に使用して静的MyNamespace.Classnameが欠落している可能性があります解決しない場合は

    • クラスMyNamespace.Classnameを分析する際にタイプ名は、クラス自体の中に必要とされていないような静的MyNamespace.Classnameは(存在する可能性があります。念頭に置いて

    ここで私が思い付いたソリューションです。さらに特別なケースがあるかもしれませんが、それは良い出発点だと思うかもしれません。

    private static IEnumerable<string> FindUsedUsings(SemanticModel model, 
          SyntaxNode node, SyntaxNode root) 
    { 
        var aliasResolution = new Dictionary<string, string>(); 
        var usings = root.DescendantNodes().OfType<UsingDirectiveSyntax>(); 
        foreach (var curr in usings) 
        { 
         var nameEquals = curr.DescendantNodes(). 
          OfType<NameEqualsSyntax>().FirstOrDefault(); 
         if (nameEquals != null) 
         { 
          var qualifiedName = 
           curr.DescendantNodes().OfType<QualifiedNameSyntax>(). 
            FirstOrDefault()?.ToFullString(); 
          if (qualifiedName != null) 
          { 
           aliasResolution.Add(nameEquals.Name.Identifier.Text, qualifiedName); 
          } 
         } 
        } 
        var currentNamespace = node.Ancestors(). 
         OfType<NamespaceDeclarationSyntax>().FirstOrDefault(); 
        var namespacename = currentNamespace?.Name.ToString(); 
        if (namespacename == null) 
        { 
         // Empty namespace 
         namespacename = string.Empty; 
        } 
        var resultList = new List<string>(); 
        foreach (var identifier in node.DescendantNodesAndSelf().OfType<TypeSyntax>()) 
        { 
         if (identifier is PredefinedTypeSyntax || identifier.IsVar) 
         { 
          // No usings required for predefined types or var... 
          // [string, int, char, var, etc. do not need usings] 
          continue; 
         } 
         // If an alias is defined use it prioritized 
         if (GetUsingFromAlias(model, identifier, aliasResolution, resultList)) 
         { 
          continue; 
         } 
         // If a type is provided, try to resolve it 
         if (GetUsingFromType(model, identifier, namespacename, resultList)) 
         { 
          continue; 
         } 
         // If no type is provided check if the expression 
         // corresponds to a static member call 
         GetUsingFromStatic(model, identifier, resultList); 
        } 
        return resultList.Distinct().ToList(); 
    } 
    
    private static void GetUsingFromStatic(SemanticModel model, TypeSyntax identifier, 
          List<string> resultList) 
    { 
        var symbol = model.GetSymbolInfo(identifier).Symbol; 
        // If the symbol (field, property, method call) can be resolved, 
        // is static and has a containing type 
        if (symbol != null && symbol.IsStatic && symbol.ContainingType != null) 
        { 
         var memberAccess = identifier.Parent as ExpressionSyntax; 
         if (memberAccess != null) 
         { 
          bool hasCallingType = false; 
          var children = memberAccess.ChildNodes(); 
          foreach (var childNode in children) 
          { 
           // If the Expression has a Type 
           // (that is, if the expression is called from an identifyable source) 
           // no using static is required 
           var typeInfo = model.GetSymbolInfo(childNode).Symbol as INamedTypeSymbol; 
           if (typeInfo != null) 
           { 
            hasCallingType = true; 
            break; 
           } 
          } 
          // if the type-Info is missing a static using is required 
          if (!hasCallingType) 
          { 
           // type three using: using static [QualifiedType] 
           resultList.Add($"static {symbol.ContainingType}"); 
          } 
         } 
        } 
    } 
    
    private static bool GetUsingFromType(SemanticModel model, TypeSyntax identifier, 
          string namespacename, List<string> resultList) 
    { 
        var typeInfo = model.GetSymbolInfo(identifier).Symbol as INamedTypeSymbol; 
        // dynamic is not required and not provided as an INamedTypeSymbol 
        if (typeInfo != null) 
        { 
         if (identifier is QualifiedNameSyntax 
          || identifier.Parent is QualifiedNameSyntax) 
         { 
          // Qualified namespaces can be ignored... 
          return true; 
         } 
         var containingNamespace = typeInfo.ContainingNamespace.ToDisplayString(); 
         // The current namespace need not be referenced 
         if (namespacename == containingNamespace) 
         { 
          return true; 
         } 
         // Type one using: using [Namespace]; 
         resultList.Add(typeInfo.ContainingNamespace.ToDisplayString()); 
         return true; 
        } 
        return false; 
    } 
    
    private static bool GetUsingFromAlias(SemanticModel model, TypeSyntax identifier, 
          Dictionary<string, string> aliasResolution, List<string> resultList) 
    { 
        var aliasInfo = model.GetAliasInfo(identifier); 
        // If the identifier is an alias 
        if (aliasInfo != null) 
        { 
         // check if it can be resolved 
         if (aliasResolution.ContainsKey(aliasInfo.Name)) 
         { 
          // Type two using: using [Alias] = [QualifiedType]; 
          resultList.Add($"{aliasInfo.Name} = {aliasResolution[aliasInfo.Name]}"); 
          return true; 
         } 
        } 
        return false; 
    } 
    
  • +1

    この特定のコードは、いくつかの点で「使用されている」型をキャッチします。私が "var x = y.z"のようなことをすれば、 'x'の名前空間を" used "にするかどうかを考えているというオリジナルのポスター(それははっきりしない)に対する質問です。私は彼らが "レイヤーXはレイヤーYに触れない"という何らかの規則をしようとしているのであれば、あなたのコードは素晴らしいスタートです。しかし、彼らは何か他のものを求めています。 –

    +0

    私が欲しいのは簡単です:私は、そのコードを実行するのに必要な使い方を知りたいと思います。使用して削除すると、そのコードにエラーが表示されます。 – TWT

    +0

    現在の回答のコードは、私が期待するものではなく、単に使用されるすべての名前空間を返します。たとえば、構文ノードに "System.Threading.Thread.Sleep(100)"が含まれている場合、リストには "System.Threading"が含まれますが、その場合は使用する必要はありません。それは次のステップで私が立ち往生している。 – TWT

    関連する問題