Как собрать все MethodDeclarationSyntax транзитивно с Roslyn? - PullRequest
0 голосов
/ 20 декабря 2018

Учитывая список 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в словаре / Hashset?

Редактировать 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 голосов
/ 20 декабря 2018

Мне интересно, правильно ли использовать SyntaxNode.

Поскольку вы уже используете SymbolFinder и используете семантическую модель, возможно, правильный путьозначает использовать ISymbol с, а не SyntaxNode с.

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>> и объединить все местоположения и таким образом восстановить синтаксисы из этого результата.

...