2016-08-11 62 views
3
中使用哪些使用指令

如何知道在給定SyntaxNode的後代中使用了哪些使用指令。查找在方法

請看下面的例子: https://dotnetfiddle.net/mCzNST 在這裏,我想知道哪些usings在Class1的使用,卻忽略了在Class2中使用的usings。

+0

這實際上比您想象的要複雜得多;去掉看起來不相關的'using'指令會影響帶lambda的複雜情況下的重載解析。 – SLaks

+0

我不想刪除使用。我只是想知道哪些使用definitally正在使用。 – TWT

+0

@SLaks的觀點是「絕對使用」很難,因爲重載決議依賴於使用清單上的微妙方式。如果「綁定類型並創建列表」的啓發式對於您的使用來說足夠好,那很好。但請注意這是一種啓發式。在Roslyn體系結構中,編譯器直接爲我們生成未使用的使用信息,因爲您無法使用我們提供的API來完成此操作。 –

回答

3

您可以使用語義模型獲取與語法節點相關的類型。類型信息知道可用於標識相關使用的相關名稱空間。迭代所有節點時,您會收到方法的返回值以及變量,屬性和字段的類型。如果限制到特定類型(例如InvocationExpressionSyntax)的節點上,您只能得到返回類型,變量類型等

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

但是,如果你想獲得你必須確定所有申報類型usings。我寫了一個(不完全)解決方案,以確定三種需要usings的:

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

您可以識別每一種類型有不同的邏輯使用。

對於第一類使用,你應該考慮的:

  • 變種不需要使用
  • PredefinedTypes(字符串,整數)要求使用無
  • 動態不需要使用
  • QualifiedTypeNames不需要使用

對於第二種使用類型,您應該考慮:

  • 雖然可以使用別名來解析類型,但您也可以使用原始名稱。使用上面的例子,你可以寫出Statement string sample = Text.Empty;

對於第三種類型的使用,您沒有該類型的標識符,因此您需要使用表達式語句查找該調用。 請注意,在下面的解決方案中,使用靜態MyNamespace.Classname將無法正確解析,因爲我沒有爲該方法的IdentifierNameSyntax提供TypeSymbol,所以無法解析該類型。

這兩個問題解決:

  • 如果類型不解決對該類當使用靜態MyNamespace.Classname可能會丟失MyNamespace.Class2
  • 如果類型解析使用無效在分析類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'的名稱空間用於或不用。我猜如果他們試圖制定「X層無法觸摸Y層」的規則,那麼你的代碼是一個很好的開始。但他們可能需要別的東西。 –

+0

我想要的很簡單:我想知道哪些使用是必要的來運行這段代碼。如果您刪除了該代碼段,則會在該代碼段中出現錯誤。 – TWT

+0

當前答案中的代碼不是我所期望的,它只是返回所有使用的名稱空間。例如,當語法節點包含「System.Threading.Thread.Sleep(100)」時,您的列表將包含「System.Threading」,但在這種情況下,不需要使用。下一步,我陷入困境。 – TWT