< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Analyzers.WaitAnyCreateGraphAnalyzer
Assembly: NexusLabs.Needlr.AgentFramework.Analyzers
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/WaitAnyCreateGraphAnalyzer.cs
Line coverage
95%
Covered lines: 87
Uncovered lines: 4
Coverable lines: 91
Total lines: 119
Line coverage: 95.6%
Branch coverage
84%
Covered branches: 39
Total branches: 46
Branch coverage: 84.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_SupportedDiagnostics()100%11100%
Initialize(...)84.78%464695.55%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/WaitAnyCreateGraphAnalyzer.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Collections.Immutable;
 3using System.Linq;
 4
 5using Microsoft.CodeAnalysis;
 6using Microsoft.CodeAnalysis.CSharp;
 7using Microsoft.CodeAnalysis.CSharp.Syntax;
 8using Microsoft.CodeAnalysis.Diagnostics;
 9
 10namespace NexusLabs.Needlr.AgentFramework.Analyzers;
 11
 12/// <summary>
 13/// Detects calls to <c>CreateGraphWorkflow</c> (which returns a MAF <c>Workflow</c>
 14/// using BSP execution) when the compilation also declares
 15/// <c>[AgentGraphNode(JoinMode = GraphJoinMode.WaitAny)]</c> for any graph.
 16/// </summary>
 17[DiagnosticAnalyzer(LanguageNames.CSharp)]
 18public sealed class WaitAnyCreateGraphAnalyzer : DiagnosticAnalyzer
 19{
 20    private const string AgentGraphNodeAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphNodeAttribute";
 21    private const string CreateGraphWorkflowMethodName = "CreateGraphWorkflow";
 22
 23    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
 6924        ImmutableArray.Create(MafDiagnosticDescriptors.WaitAnyIncompatibleWithCreateGraphWorkflow);
 25
 26    public override void Initialize(AnalysisContext context)
 27    {
 1128        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 1129        context.EnableConcurrentExecution();
 30
 1131        context.RegisterCompilationStartAction(compilationContext =>
 1132        {
 633            var waitAnyGraphNames = new ConcurrentBag<string>();
 634            var createGraphCalls = new ConcurrentBag<(Location Location, string GraphName)>();
 1135
 636            compilationContext.RegisterSymbolAction(symbolContext =>
 637            {
 11138                var namedType = (INamedTypeSymbol)symbolContext.Symbol;
 43239                foreach (var attr in namedType.GetAttributes())
 640                {
 10541                    if (attr.AttributeClass?.ToDisplayString() != AgentGraphNodeAttributeName)
 642                        continue;
 643
 544                    string? graphName = null;
 545                    if (attr.ConstructorArguments.Length >= 1 &&
 546                        attr.ConstructorArguments[0].Value is string gn)
 647                    {
 548                        graphName = gn;
 649                    }
 650
 2051                    foreach (var named in attr.NamedArguments)
 652                    {
 553                        if (named.Key == "JoinMode" &&
 554                            named.Value.Value is int joinModeValue &&
 555                            joinModeValue == 1)
 656                        {
 457                            if (graphName is not null)
 658                            {
 459                                waitAnyGraphNames.Add(graphName);
 660                            }
 661                        }
 662                    }
 663                }
 11764            }, SymbolKind.NamedType);
 1165
 666            compilationContext.RegisterSyntaxNodeAction(syntaxContext =>
 667            {
 1868                var invocation = (InvocationExpressionSyntax)syntaxContext.Node;
 669
 1870                string? methodName = null;
 1871                if (invocation.Expression is MemberAccessExpressionSyntax memberAccess)
 672                {
 1873                    methodName = memberAccess.Name.Identifier.Text;
 674                }
 075                else if (invocation.Expression is IdentifierNameSyntax identifier)
 676                {
 077                    methodName = identifier.Identifier.Text;
 678                }
 679
 1880                if (methodName != CreateGraphWorkflowMethodName)
 1281                    return;
 682
 683                var symbolInfo = syntaxContext.SemanticModel.GetSymbolInfo(invocation, syntaxContext.CancellationToken);
 684                if (symbolInfo.Symbol is not IMethodSymbol methodSymbol)
 085                    return;
 686
 687                var containingType = methodSymbol.ContainingType;
 688                if (containingType?.Name != "IWorkflowFactory" &&
 689                    containingType?.ToDisplayString() != "NexusLabs.Needlr.AgentFramework.IWorkflowFactory")
 190                    return;
 691
 592                if (invocation.ArgumentList.Arguments.Count < 1)
 093                    return;
 694
 595                var firstArg = invocation.ArgumentList.Arguments[0].Expression;
 596                var constantValue = syntaxContext.SemanticModel.GetConstantValue(firstArg);
 597                if (constantValue.HasValue && constantValue.Value is string graphName)
 698                {
 599                    createGraphCalls.Add((invocation.GetLocation(), graphName));
 6100                }
 11101            }, SyntaxKind.InvocationExpression);
 11102
 6103            compilationContext.RegisterCompilationEndAction(endContext =>
 6104            {
 6105                var waitAnyNames = new System.Collections.Generic.HashSet<string>(waitAnyGraphNames);
 22106                foreach (var (location, graphName) in createGraphCalls)
 6107                {
 5108                    if (waitAnyNames.Contains(graphName))
 6109                    {
 2110                        endContext.ReportDiagnostic(Diagnostic.Create(
 2111                            MafDiagnosticDescriptors.WaitAnyIncompatibleWithCreateGraphWorkflow,
 2112                            location,
 2113                            graphName));
 6114                    }
 6115                }
 12116            });
 17117        });
 11118    }
 119}