如何知道在给定SyntaxNode的后代中使用了哪些使用指令。查找在方法
请看下面的例子: https://dotnetfiddle.net/mCzNST 在这里,我想知道哪些usings在Class1的使用,却忽略了在Class2中使用的usings。
如何知道在给定SyntaxNode的后代中使用了哪些使用指令。查找在方法
请看下面的例子: https://dotnetfiddle.net/mCzNST 在这里,我想知道哪些usings在Class1的使用,却忽略了在Class2中使用的usings。
您可以使用语义模型获取与语法节点相关的类型。类型信息知道可用于标识相关使用的相关名称空间。迭代所有节点时,您会收到方法的返回值以及变量,属性和字段的类型。如果限制到特定类型(例如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;
您可以识别每一种类型有不同的逻辑使用。
对于第一类使用,你应该考虑的:
对于第二种使用类型,您应该考虑:
对于第三种类型的使用,您没有该类型的标识符,因此您需要使用表达式语句查找该调用。 请注意,在下面的解决方案中,使用静态MyNamespace.Classname将无法正确解析,因为我没有为该方法的IdentifierNameSyntax提供TypeSymbol,所以无法解析该类型。
这两个问题解决:
考虑到这一点,我想出了这个解决方案。可能还有其他特殊情况要考虑,但我认为这是一个不错的起点:
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;
}
这个特定的代码会捕获某些方面正在“使用”的类型。对于原始的海报(不清楚),如果我做了像“var x = y.z”这样的东西,我们是否会考虑将'x'的名称空间用于或不用。我猜如果他们试图制定“X层无法触摸Y层”的规则,那么你的代码是一个很好的开始。但他们可能需要别的东西。 –
我想要的很简单:我想知道哪些使用是必要的来运行这段代码。如果您删除了该代码段,则会在该代码段中出现错误。 – TWT
当前答案中的代码不是我所期望的,它只是返回所有使用的名称空间。例如,当语法节点包含“System.Threading.Thread.Sleep(100)”时,您的列表将包含“System.Threading”,但在这种情况下,不需要使用。下一步,我陷入困境。 – TWT
这实际上比您想象的要复杂得多;去掉看起来不相关的'using'指令会影响带lambda的复杂情况下的重载解析。 – SLaks
我不想删除使用。我只是想知道哪些使用definitally正在使用。 – TWT
@SLaks的观点是“绝对使用”很难,因为重载决议依赖于使用清单上的微妙方式。如果“绑定类型并创建列表”的启发式对于您的使用来说足够好,那很好。但请注意这是一种启发式。在Roslyn体系结构中,编译器直接为我们生成未使用的使用信息,因为您无法使用我们提供的API来完成此操作。 –