获取所有方法调用

Get all method calls

假设我有一个简单的项目

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) { }
}

如何使用 VSSDK 提取 AddItem(new Item());?我想知道传递给它的参数是什么以及它在文本编辑器中的哪一行。

我尝试寻找 CodeElement.Kind,但不幸的是 vsCMElement.vsCMElementFunctionInvokeStmt 没有 return 任何东西。有没有其他方法可以提取此信息?

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;
}

对于 Visual Studio 的现代版本,如果您对语言服务感兴趣,那么您不必使用 DTE 和旧的 FileCodeModel 东西,但您可以利用 The .NET Compiler Platform SDK(又名 Roslyn)现在是解析 backbone。

因此,首先要做的是将最新的 Microsoft.CodeAnalysis.CSharp.Workspaces nuget package (roslyn for C#) and the Microsoft.VisualStudio.LanguageServices nuget(Visual Studio Roslyn 工作区)添加到您的包项目中。请注意,您可能需要修复 now-usual 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;
                ...
            }
        }
    }
}