如何使用Roslyn传递性地收集所有MethodDeclarationSyntax?

时间:2018-12-20 10:18:08

标签: c# roslyn

给出MethodDeclarationSyntax的列表,我想收集可传递调用此方法的解决方案中的所有方法。

我一直在使用以下代码:

        var methods = new Stack<MethodDeclarationSyntax>();
        ... // fill methods with original method to start from
        var visited = new HashSet<MethodDeclarationSyntax>();
        while (methods.Count > 0)
        {
            var method = methods.Pop();
            if (!visited.Add(method))
            {
                continue;
            }

            var methodSymbol = (await solution.GetDocument(method.SyntaxTree).GetSemanticModelAsync()).GetDeclaredSymbol(method);
            foreach (var referencer in await SymbolFinder.FindCallersAsync(methodSymbol, solution))
            {
                var callingMethod = (MethodDeclarationSyntax) referencer.CallingSymbol.DeclaringSyntaxReferences[0].GetSyntax();
                methods.Push(callingMethod);
            }
        }

问题在于MethodDeclarationSyntax似乎不是一个单例,因此此循环永远运行,一次又一次地访问相同的方法。

在字典/哈希集中唯一标识MethodDeclarationSyntax的正确方法是什么?

编辑1)

作为一种解决方法,我正在使用以下MethodDeclarationSyntaxComparer初始化我的HashSet,但是它看起来非常脆弱:

    private class MethodDeclarationSyntaxComparer: IEqualityComparer<MethodDeclarationSyntax>
    {
        public bool Equals(MethodDeclarationSyntax x, MethodDeclarationSyntax y)
        {
            var xloc = x.GetLocation();
            var yloc = y.GetLocation();
            return xloc.SourceTree.FilePath == yloc.SourceTree.FilePath &&
                   xloc.SourceSpan == yloc.SourceSpan;
        }

        public int GetHashCode(MethodDeclarationSyntax obj)
        {
            var loc = obj.GetLocation();
            return (loc.SourceTree.FilePath.GetHashCode() * 307) ^ loc.SourceSpan.GetHashCode();
        }
    }

1 个答案:

答案 0 :(得分:1)

我想知道是否在这里使用SyntaxNode是正确的方法。

由于您已经在使用SymbolFinder并且正在使用语义模型,所以正确的方法也许是实际使用ISymbol而不是SyntaxNode。 / p>

ISymbol已包含您正在使用的SyntaxReference,因此:

   var methods = new Stack<IMethodSymbol>();
    ... // fill methods with original method to start from
    ... // convert methods to symbols via semanticModel.GetDeclaredSymbol (node);
    var visited = new HashSet<IMethodSymbol>();
    while (methods.Count > 0)
    {
        var method = methods.Pop();
        if (!visited.Add(method))
        {
            continue;
        }

        foreach (var referencer in await SymbolFinder.FindCallersAsync(method, solution))
        {
            var callingMethod = (MethodDeclarationSyntax) referencer.CallingSymbol.DeclaringSyntaxReferences[0].GetSyntax();
            methods.Push(callingMethod);
        }
    }

您可以将访问的哈希集设为Dictionary<IMethodSymbol, IEnumerable<Location>>,并合并所有位置,从而从该结果中重构语法。