Подтвердить что ты не робот

Получение аргументов метода с 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)).

4b9b3361

Ответ 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);
   }
}