使用Roslyn,从字符串文字参数中查找调用方法

时间:2017-04-28 11:58:17

标签: c# vb.net roslyn

我尝试使用Roslyn替换实用程序中的一些旧的慢代码,该实用程序在源代码中搜索未包含在X()函数调用中的字符串文字(包含在{{1将被翻译)。

我能够非常轻松地使用语法树来获取字符串文字,并识别出X()中包含它们的地方的大多数。我在做什么:给定一个X()对象,我发现这给了我函数调用,我可以将它与正则表达式匹配。

LiteralExpressionSyntax

当字符串文字在两行之间分开时,我很快就遇到了问题。那时我意识到我检查它是否在s.Parent.Parent.Parent.ToFullString()电话中的方法很差,因为我必须不断向链中添加X()。虽然我可以写一些东西向后爬树,但这似乎不是正确的方式(可能不会很好)。

我一直试图找到一种方法,给定一个字符串文字语法节点,以确定它是否是方法调用中的参数。我还没有找到一种从语法树到语义模型的合适方式来找到我正在寻找的东西。我甚至不确定这是正确的方法,还是我错过了一些明显的东西。

我能够使用.Parent来获取符号,但是它遇到了同样的问题 - 我不能只传递字符串文字参数的位置,我必须通过(正确)父母,我回到了我开始的地方。

我希望避免多次遍历语法树,因为这样会减慢速度。我可以在大约1秒钟内解析553个文件,但是当我尝试循环以解决这些多行情况时,我的时间大约为12-13秒。

万一我在这部中篇小说中失去了你(对不起),这里是我希望弄清楚的:,因为字符串字面值被传递为一个方法的参数,是否有一种简单的方法来确定该方法是什么?

以下是一些示例代码 - 我用SymbolFinder.FindSymbolAtPositionAsync替换了对X()函数的调用,只是为了模拟我搜索的代码(我必须添加对其中一个的引用)我们的DLL,所以我将调用切换到Convert.ToString,所以我可以在这个例子中引用mscorlib。

Convert.ToString()

1 个答案:

答案 0 :(得分:1)

警告说我之前从未使用过Roslyn,而且我通常使用VB.NET编写代码,我认为我将一些看起来像你想要的东西混在一起(使用LINQPad和许多Dump()调用帮助找出发生了什么。)

void TestAttempt()
{

    string source = @"
  Imports System
  Namespace Exceptions
    Public NotInheritable Class ExampleException
      Inherits Validation

      Public Sub New()
        X(""Ignore me 1"")
        Console.WriteLine(""Report me"")
        Console.WriteLine(X(""Ignore me 2""))

        MyBase.New(X(""Ignore me 3, "" & _
                   ""Because I'm already translated.""))
      End Sub
    End Class
  End Namespace";

    var tree = VisualBasicSyntaxTree.ParseText(source);

    var syntaxRoot = tree.GetRoot();
    int i = 0, notWrapped = 0;

    foreach (var s in syntaxRoot.DescendantNodes().OfType<LiteralExpressionSyntax>())
    {
        // things to skip:
        if (s == null) { continue; }
        if (s.Kind() != SyntaxKind.StringLiteralExpression) { continue; }

        if (!IsWrappedInCallToX(s))
        {
            Console.WriteLine($"  Reported: {s.ToString()}");
            notWrapped++;
        }
        i++;
    }

    Console.WriteLine();
    Console.WriteLine($"Total: {i}, Not Wrapped In X: {notWrapped}");
}

bool IsWrappedInCallToX(SyntaxNode node)
{
    var invocation = node as InvocationExpressionSyntax;
    if (invocation != null)
    {
        var exp = invocation.Expression as IdentifierNameSyntax;
        if (exp != null && exp.ToString() == "X")
        {
            return true;
        }
    }
    if (node.Parent != null)
    {
        return IsWrappedInCallToX(node.Parent);
    }
    return false;
}

这导致:

  Reported: "Report me"

Total: 5, Not Wrapped In X: 1

IsWrappedInCallToX函数只是递归树,为InvocationExpressionSyntax函数寻找X。我知道你说过#34;虽然我可以写一些东西向后爬上树,但这似乎不是正确的方式(并且可能不会表现得很好)&#34;但对我来说,这似乎是正确的方式 - 如果你的代码基础上的表现很糟糕,也许不是!

同样,我对罗斯林一无所知(这听起来很有趣),所以这很可能是一个糟糕的解决方案! : - )