2016-08-11 26 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