Получение аргументов метода с Roslyn
Я могу получить список из решения всех вызовов специальному методу, используя следующий код:
var createCommandList = new List<MethodSymbol>();
INamedTypeSymbol interfaceSymbol =
(from p
in solution.Projects
select p.GetCompilation().GetTypeByMetadataName(
"BuySeasons.BsiServices.DataResource.IBsiDataConnection")
).FirstOrDefault();
foreach (ISymbol symbol in interfaceSymbol.GetMembers("CreateCommand"))
{
if (symbol.Kind == CommonSymbolKind.Method
&& symbol is MethodSymbol)
{
createCommandList.Add(symbol as MethodSymbol);
}
}
foreach (MethodSymbol methodSymbol in createCommandList)
{
foreach (ReferencedSymbol referenceSymbol
in methodSymbol.FindReferences(solution))
{
foreach (ReferenceLocation referenceLocation
in from l
in referenceSymbol.Locations
orderby l.Document.FilePath
select l)
{
if (referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Line ==
referenceLocation.Location.GetLineSpan(false)
.EndLinePosition.Line)
{
Debug.WriteLine("{0} {1} at {2} {3}/{4} - {5}",
methodSymbol.Name,
"(" + String.Join(",",
(from p
in methodSymbol.Parameters
select p.Type.Name + " " + p.Name).ToArray()
) + ")",
Path.GetFileName(referenceLocation.Location.GetLineSpan(false)
.Path),
referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Line,
referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Character,
referenceLocation.Location.GetLineSpan(false)
.EndLinePosition.Character));
}
else
{
throw new ApplicationException("Call spans multiple lines");
}
}
}
}
Но это дает мне список ReferencedSymbol
. Хотя это дает мне номер файла и строки, из которого вызывается метод, я также хотел бы получить конкретные аргументы, с которыми вызван метод. Как я могу либо преобразовать то, что у меня есть, либо получить ту же информацию с Roslyn? (обратите внимание, что я сначала загружаю решение с помощью метода Solution.Load
, а затем прокручиваю, чтобы узнать, где метод определен/объявлен (createCommandList
)).
Ответы
Ответ 1
Вы уже используете Roslyn здесь. Когда у вас есть referenceSymbol
, вы можете получить синтаксис декларации метода, а затем посмотреть вниз в дерево, чтобы получить список параметров.
Я вставил переменную arguments
, которая использует ваш referenceSymbol
:
// Snip start
foreach (MethodSymbol methodSymbol in createCommandList)
{
foreach (ReferencedSymbol referenceSymbol
in methodSymbol.FindReferences(solution))
{
var arguments = referenceSymbol.Definition.DeclaringSyntaxNodes.First()
.DescendantNodes().OfType<ParameterSyntax>().ToList();
foreach (ReferenceLocation referenceLocation in
from l
in referenceSymbol.Locations
orderby l.Document.FilePath
select l)
{
// Snip end
Когда вы выполняете вывод Debug, вы можете использовать этот список аргументов для получения имен.
Мое решение требует получения First()
DeclaringSyntaxNodes
, который мне не очень нравится, но не может найти другого способа получить узлы потомков дерева синтаксиса.
Ответ 2
Вы можете выполнить поиск в дереве синтаксиса ссылки в определенном исходном местоположении, чтобы найти node, который вы ищете. Вам нужно будет использовать вызов типа DescendentNodes из корня дерева node, и вам, вероятно, потребуется запросить тип, специфичный для языка node, который вы ищете. Когда у вас есть node в дереве реферирования, вы можете использовать эту семантическую модель дерева, чтобы сообщить вам другую информацию о аргументах.
Ответ 3
Я обнаружил другой способ получить список параметров из метода, который может работать и для других.
Скажем, у меня есть следующий метод, который имеет два параметра:
public string DebugPage(string enabled, string show){
//do stuff
}
Затем вы получаете свои узлы, как хотите. Например, это дает мне список общедоступных методов:
IEnumerable<MethodDeclarationSyntax> methods = from m in root.DescendantNodes().OfType<MethodDeclarationSyntax>() where m.Modifiers.ToString().Contains("public") select m;
Затем я могу выполнить итерацию через этот список методов, чтобы получить свойство ParameterList, которое позволяет сделать эту операцию очень простой. В конце этого цикла список parameters
будет содержать имена каждого параметра в методе (в примере метода Debug
выше он будет содержать значения enabled
и show
, как ожидалось):
var parameters = new List<string>();
foreach (var method in methods)
{
foreach (var n in method.ParameterList.Parameters)
{
var parameterName = n.Identifier.Text;
parameters.Add(parameterName);
}
}