Получить все вызовы методов

Допустим, у меня есть простой проект

class Program : TestBase
{
    static void Main(string[] args)
    {

    }

    public void Test()
    {
        AddItem(new Item());
        AddItem(new Item());
    }
}

public class Item { }

public class TestBase
{
    public virtual void AddItem(Item vertex) { }
}

Как извлечь AddItem(new Item()); с помощью VSSDK? Я хочу знать, какие параметры ему передаются и в какой строке он находится в текстовом редакторе.

Я пытался найти CodeElement.Kind, но, к сожалению, vsCMElement.vsCMElementFunctionInvokeStmt ничего не возвращает. Есть ли другой способ извлечь эту информацию?

public static async Task InitializeAsync(AsyncPackage package)
{
    // ...
    _dte = (await package.GetServiceAsync(typeof(DTE))) as DTE2;

    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
    var sandboxProject = _dte.Solution.Projects;

    var codeItems = new List<string>();

    if (!ThreadHelper.CheckAccess())
        return;

    foreach (Project project in sandboxProject)
    {
        var projectItems = GetProjectItemsRecursively(project.ProjectItems);
        foreach (ProjectItem projectItem in projectItems)
        {
            foreach (CodeElement element in projectItem.FileCodeModel.CodeElements)
            {
                codeItems.AddRange(GetItems(element).Select(codeItem => $"{codeItem.FullName} : {codeItem.Kind.ToString()}"));
            }
        }
    }
}

private static IEnumerable<CodeElement> GetItems(CodeElement items)
{
    var ret = new List<CodeElement>();
    if (items == null)
        return ret;

    foreach (CodeElement item in items.Children)
    {
        ret.Add(item);
        ret.AddRange(GetItems(item));
    }

    return ret;
}

private static List<ProjectItem> GetProjectItemsRecursively(ProjectItems items)
{
    var ret = new List<EnvDTE.ProjectItem>();
    if (items == null) return ret;
    foreach (ProjectItem item in items)
    {
        ret.Add(item);
        ret.AddRange(GetProjectItemsRecursively(item.ProjectItems));
    }
    return ret;
}

Чтобы знать количество параметров метода, требуется синтаксический анализатор. Так что не VSSDK, у него нет парсера. Такая функция, как подсветка синтаксиса, основана на лексическом анализе. Roslyn — это стандартное решение для анализа кода C#, если вам все равно нужно анализировать только код C# или VB.NET.

Hans Passant 22.07.2019 13:23

@HansPassant Спасибо, мой код вернул параметр args для static void Main(string[] args), поэтому я предположил, что он также сможет возвращать вызовы методов внутри метода.

FCin 22.07.2019 13:39

Какая версия Visual Studio?

Simon Mourier 23.07.2019 13:50

@SimonMourier Сообщество VS2019

FCin 23.07.2019 14:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
4
1 042
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В современной версии Visual Studio, если вы заинтересованы в языковых службах, вам не нужно использовать DTE и старый материал FileCodeModel, но вы можете использовать Пакет SDK для платформы компилятора .NET (также известный как Roslyn), который теперь является основой синтаксического анализа.

Итак, первое, что нужно сделать, это добавить последний пакет nuget Microsoft.CodeAnalysis.CSharp.Workspaces (roslyn для C#) и nuget Microsoft.VisualStudio.LanguageServices (рабочее пространство Visual Studio Roslyn) в проект пакета. Обратите внимание, что вам, возможно, придется исправить обычный беспорядок nuget...

Как только это будет сделано, вы можете написать такой код вместо своего:

// get component model & Visual Studio Roslyn workspace
var componentModel = await package.GetServiceAsync<SComponentModel, IComponentModel>();
var workspace = componentModel.GetService<VisualStudioWorkspace>(); // requires "Microsoft.VisualStudio.LanguageServices" nuget package

// enum all the projects
foreach (var project in workspace.CurrentSolution.Projects)
{
    // enum all the documents in the project
    foreach (var doc in project.Documents)
    {
        // get the semantic model & syntax tree root
        var model = await doc.GetSemanticModelAsync();
        var root = await model.SyntaxTree.GetRootAsync();

        // find a class named "TestBase"
        // ClassDeclarationSyntax etc. requires "Microsoft.CodeAnalysis.CSharp.Workspaces" nuget package
        var myClass = root.DescendantNodes()
            .OfType<ClassDeclarationSyntax>()
            .FirstOrDefault(c => c.Identifier.Text == "TestBase");
        if (myClass != null)
        {
            // find a method named "AddItem"
            var myMethod = myClass.Members.Where(m => m.Kind() == SyntaxKind.MethodDeclaration)
                .OfType<MethodDeclarationSyntax>()
                .FirstOrDefault(m => m.Identifier.Text == "AddItem");
            if (myMethod != null)
            {
                // get the list of method parameters
                var parameters = myMethod.ParameterList.Parameters;
                ...

                // get the start line for the method declaration
                var lineSpan = model.SyntaxTree.GetLineSpan(myMethod.Span);
                int startLine = lineSpan.StartLinePosition.Line;
                ...
            }
        }
    }
}

Это выглядит красиво, я попробую, как только вернусь домой.

FCin 23.07.2019 16:02

Другие вопросы по теме