web-dev-qa-db-fra.com

Comment collecter toutes les MethodDeclarationSyntax de manière transitoire avec Roslyn?

Etant donné une liste de MethodDeclarationSyntax, j'aimerais collecter toutes les méthodes d'une solution appelant cette méthode de manière transitoire.

J'ai utilisé le code suivant:

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

Le problème est que MethodDeclarationSyntax ne semble pas être un singleton, donc cette boucle est exécutée pour toujours, visitant les mêmes méthodes encore et encore.

Quelle est la manière appropriée d’identifier de manière unique une MethodDeclarationSyntax dans un dictionnaire/hachage?

Modifier 1)

En guise de solution de contournement, j'utilise la MethodDeclarationSyntaxComparer suivante pour initialiser ma HashSet, mais elle semble très fragile:

    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();
        }
    }
7
xoofx

Je me demande si utiliser SyntaxNode ici est la bonne façon de faire.

Puisque vous utilisez déjà SymbolFinder et que vous utilisez le modèle sémantique, la meilleure façon de procéder consiste peut-être à utiliser ISymbols, plutôt que SyntaxNodes.

ISymbol contient déjà la SyntaxReferences que vous utilisez, donc:

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

Vous pouvez éventuellement transformer le hashset visité en un Dictionary<IMethodSymbol, IEnumerable<Location>>, concaténer tous les emplacements et reconstruire ainsi les syntaxes à partir de ce résultat.

1
Marius Ungureanu