罗斯林 - 找到所有符号

时间:2017-04-18 10:25:16

标签: c# roslyn roslyn-code-analysis

我使用以下代码检索代码块中使用的所有符号。这包括声明和符号引用。 不幸的是,GetSymbolInfo调用非常慢,因此这个方法花费的总时间可能很长。有没有办法加快速度呢?

    public static IEnumerable<ISymbol> GetAllSymbols(CSharpCompilation compilation, SyntaxNode root)
    {
        var noDuplicates = new HashSet<ISymbol>();

        var model = compilation.GetSemanticModel(root.SyntaxTree);

        foreach (var node in root.DescendantNodesAndSelf())
        {
            ISymbol symbol = model.GetDeclaredSymbol(node) ??
                model.GetSymbolInfo(node).Symbol;

            if (symbol != null)
            {
                if (noDuplicates.Add(symbol))
                    yield return symbol; 
            }
        }
    }

1 个答案:

答案 0 :(得分:4)

我发现您的代码存在一些问题。

第一个是一个细节,但你说你正在寻找你“使用”的符号。您是否认为使用符号将符号声明为?如果没有,你可以摆脱model.GetDeclaredSymbol(node)

第二个问题更为重要:你经常得到相同的符号。

以下面的陈述为例:

SomeMethod();

这是一个ExpressionStatement节点,其中有一个InvocationExpression,其中有一个IdentifierName。您在所有这三个节点上调用model.GetSymbolInfo(node)。你应该寻找一种避免这种情况的方法。

如果您只在类型model.GetSymbolInfo(node)(或其后代,SimpleNameSyntaxIdentifierNameSyntax)的节点上调用GenericNameSyntax,您会获得很多符号。

类似的东西:

public static IEnumerable<ISymbol> GetAllSymbols(CSharpCompilation compilation, SyntaxNode root)
{
    var noDuplicates = new HashSet<ISymbol>();

    var model = compilation.GetSemanticModel(root.SyntaxTree);

    foreach (var node in root.DescendantNodesAndSelf())
    {
        switch (node.Kind())
        {
            case SyntaxKind.IdentifierName:
            case SyntaxKind.GenericName:
                ISymbol symbol = model.GetSymbolInfo(node).Symbol;

                if (symbol != null && noDuplicates.Add(symbol))
                {
                    yield return symbol;
                }
                break;
        }
    }
}

但它不会获得所有符号。例如,找不到运算符的符号。

这让我想到了第三点:你真的应该考虑你追求的符号。你真的需要所有符号吗?

即使答案是“是”,通过改变上述逻辑,你可以避免许多冗余查找的情况。

例如:

public static IEnumerable<ISymbol> GetAllSymbols(CSharpCompilation compilation, SyntaxNode root)
{
    var noDuplicates = new HashSet<ISymbol>();

    var model = compilation.GetSemanticModel(root.SyntaxTree);

    foreach (var node in root.DescendantNodesAndSelf())
    {
        switch (node.Kind())
        {
            case SyntaxKind.ExpressionStatement:
            case SyntaxKind.InvocationExpression:
                break;
            default:
                ISymbol symbol = model.GetSymbolInfo(node).Symbol;

                if (symbol != null)
                {
                    if (noDuplicates.Add(symbol))
                        yield return symbol;
                }
                break;
        }
    }
}

在此内容中,我仅过滤了上述示例中的ExpressionStatementInvocationExpression。还有更多你可以安全地过滤掉,但我把它作为练习留给你。