与えられたSyntaxNodeの子孫でどのディレクティブが使用されているかをどのように知ることができますか?どのディレクティブがメソッドで使用されているかを見つける
次の例を参照してください。 https://dotnetfiddle.net/mCzNST ここでは、Class1で使用されている使用法を知りたいが、Class2で使用されている使用法は無視したい。
与えられたSyntaxNodeの子孫でどのディレクティブが使用されているかをどのように知ることができますか?どのディレクティブがメソッドで使用されているかを見つける
次の例を参照してください。 https://dotnetfiddle.net/mCzNST ここでは、Class1で使用されている使用法を知りたいが、Class2で使用されている使用法は無視したい。
セマンティックモデルを使用して、構文ノードに関連するタイプを取得できます。タイプ情報は、関連する使用法を識別するために使用できる関連する名前空間を認識します。すべてのノードを反復処理するときには、メソッドの戻り値と変数、プロパティ、およびフィールドの型が返されます。あなたは、特定のタイプ(例えば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;
異なるロジックで使用しているすべてのタイプを識別することができます。あなたが考慮すべき使用しての最初のタイプの場合
:
第2の種類の使用については、次の点を考慮する必要があります。
タイプの識別子がないため、Expression Statementを使用して呼び出しを見つける必要があります。 以下のソリューションでは、メソッドのIdentifierNameSyntaxにTypeSymbolが指定されていないため、静的なMyNamespace.Classnameを使用すると正しく解決されないため、型を解決できませんでした。
これは、2つの問題が解決することができますタイプは使用して無効を解決した場合は型がクラスMyNamespace.Class2
ここで私が思い付いたソリューションです。さらに特別なケースがあるかもしれませんが、それは良い出発点だと思うかもしれません。
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'の名前空間を" used "にするかどうかを考えているというオリジナルのポスター(それははっきりしない)に対する質問です。私は彼らが "レイヤーXはレイヤーYに触れない"という何らかの規則をしようとしているのであれば、あなたのコードは素晴らしいスタートです。しかし、彼らは何か他のものを求めています。 –
私が欲しいのは簡単です:私は、そのコードを実行するのに必要な使い方を知りたいと思います。使用して削除すると、そのコードにエラーが表示されます。 – TWT
現在の回答のコードは、私が期待するものではなく、単に使用されるすべての名前空間を返します。たとえば、構文ノードに "System.Threading.Thread.Sleep(100)"が含まれている場合、リストには "System.Threading"が含まれますが、その場合は使用する必要はありません。それは次のステップで私が立ち往生している。 – TWT
これは実際には思ったよりもはるかに複雑です。一見無関係な 'using'ディレクティブを削除すると、lambdasの複雑な場合のオーバーロード解決に影響する可能性があります。 – SLaks
私は使い方を削除したくありません。私はちょうどどの使用が確実に使用されているか知りたい。 – TWT
@SLaksのポイントは、過負荷の解決は使用方法の微妙な方法に依存するため、「間違いなく使用」が難しいことです。 「タイプをバインドしてリストを作成する」というヒューリスティックがあなたの使用に十分適しているなら、それは問題ありません。しかし、ヒューリスティックであることに注意してください。 Roslynアーキテクチャーでは、コンパイラは、私たちが提供するAPIを使って実行できないため、使用されていない使用情報を直接生成します。 –