src/WebJobs.Extensions.DurableTask.Analyzers/Analyzers/Orchestrator/OrchestratorMethodCollector.cs (75 lines of code) (raw):

// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System.Collections.Generic; using System.Linq; namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.Analyzers { /// <summary> /// This class collects all orchestator methods and methods used within orchestrators defined in the solution. /// This is meant to be used with a DiagnosticAnalyzer by registering actions on SyntaxKind.MethodDeclaration. /// </summary> public class OrchestratorMethodCollector { private readonly Dictionary<ISymbol, MethodInformation> orchestratorMethodDeclarations = new Dictionary<ISymbol, MethodInformation>(SymbolEqualityComparer.Default); /// <summary> /// Method used to collect orchestrator methods and methods used in orchestrators that are defined in /// the solution. Call this on a DiagnosticAnalyzer by registering actions on SyntaxKind.MethodDeclaration. /// </summary> /// <param name="context"></param> public void FindOrchestratorMethods(SyntaxNodeAnalysisContext context) { var semanticModel = context.SemanticModel; var symbol = context.ContainingSymbol; if (symbol != null && context.Node is MethodDeclarationSyntax declaration && SyntaxNodeUtils.IsInsideOrchestratorFunction(semanticModel, declaration)) { var methodInformation = new MethodInformation() { SemanticModel = semanticModel, Declaration = declaration, DeclarationSymbol = symbol, }; if (!this.orchestratorMethodDeclarations.ContainsKey(symbol)) { this.orchestratorMethodDeclarations.Add(symbol, methodInformation); this.FindInvokedMethods(semanticModel, methodInformation); } } } private void FindInvokedMethods(SemanticModel semanticModel, MethodInformation parentMethodInformation) { var parentDeclaration = parentMethodInformation.Declaration; var invocationExpressions = parentDeclaration.DescendantNodes().OfType<InvocationExpressionSyntax>(); foreach (var invocation in invocationExpressions) { if (SyntaxNodeUtils.TryGetDeclaredSyntaxNode(semanticModel, invocation, out SyntaxNode invokedMethodDeclaration) && invokedMethodDeclaration is MethodDeclarationSyntax && SyntaxNodeUtils.TryGetISymbol(semanticModel, invocation, out ISymbol invokedSymbol)) { if (this.orchestratorMethodDeclarations.TryGetValue(invokedSymbol, out MethodInformation existingMethodInformation)) { existingMethodInformation.Invocations.Add(invocation); if (!existingMethodInformation.Equals(parentMethodInformation)) { existingMethodInformation.Parents.Add(parentMethodInformation); } } else { if (SyntaxNodeUtils.TryGetSemanticModelForSyntaxTree(semanticModel, invokedMethodDeclaration, out SemanticModel invocationModel)) { var invokedMethodInformation = new MethodInformation() { SemanticModel = invocationModel, Declaration = invokedMethodDeclaration, DeclarationSymbol = invokedSymbol, Invocations = new List<InvocationExpressionSyntax>() { invocation }, Parents = new HashSet<MethodInformation>(new List<MethodInformation>() { parentMethodInformation }), }; this.orchestratorMethodDeclarations.Add(invokedSymbol, invokedMethodInformation); FindInvokedMethods(invocationModel, invokedMethodInformation); } } } } } /// <summary> /// Gets a collection of methods stored in this OrchestratorMethodCollector. /// Call this as a CompilationEndAction on the DiagnosticAnalyzer using an instance of this class. /// </summary> /// <returns>The collection of orchestrator methods and methods used within orchestrator functions.</returns> public IEnumerable<MethodInformation> GetOrchestratorMethods() { return orchestratorMethodDeclarations.Values; } } }