如何知道在給定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來完成此操作。 –