< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Analyzers.AgentOrphanAnalyzer
Assembly: NexusLabs.Needlr.AgentFramework.Analyzers
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/AgentOrphanAnalyzer.cs
Line coverage
100%
Covered lines: 87
Uncovered lines: 0
Coverable lines: 87
Total lines: 121
Line coverage: 100%
Branch coverage
93%
Covered branches: 41
Total branches: 44
Branch coverage: 93.1%
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(...)93.18%4444100%

File(s)

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

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Collections.Immutable;
 3
 4using Microsoft.CodeAnalysis;
 5using Microsoft.CodeAnalysis.Diagnostics;
 6
 7namespace NexusLabs.Needlr.AgentFramework.Analyzers;
 8
 9/// <summary>
 10/// Analyzer that detects agent types that participate in no topology declaration.
 11/// </summary>
 12/// <remarks>
 13/// <b>NDLRMAF008</b> (Info): A class decorated with <c>[NeedlrAiAgent]</c> is not referenced in any
 14/// topology attribute (<c>[AgentHandoffsTo]</c>, <c>[AgentGroupChatMember]</c>, or
 15/// <c>[AgentSequenceMember]</c>). This may indicate an orphaned or work-in-progress agent.
 16/// </remarks>
 17[DiagnosticAnalyzer(LanguageNames.CSharp)]
 18public sealed class AgentOrphanAnalyzer : DiagnosticAnalyzer
 19{
 20    private const string NeedlrAiAgentAttributeName = "NexusLabs.Needlr.AgentFramework.NeedlrAiAgentAttribute";
 21    private const string AgentHandoffsToAttributeName = "NexusLabs.Needlr.AgentFramework.AgentHandoffsToAttribute";
 22    private const string AgentGroupChatMemberAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGroupChatMemberAttrib
 23    private const string AgentSequenceMemberAttributeName = "NexusLabs.Needlr.AgentFramework.AgentSequenceMemberAttribut
 24    private const string AgentGraphEdgeAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphEdgeAttribute";
 25    private const string AgentGraphEntryAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphEntryAttribute";
 26    private const string AgentGraphNodeAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphNodeAttribute";
 27    private const string AgentGraphReducerAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphReducerAttribute";
 28
 29    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
 18530        ImmutableArray.Create(MafDiagnosticDescriptors.OrphanAgent);
 31
 32    public override void Initialize(AnalysisContext context)
 33    {
 2734        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 2735        context.EnableConcurrentExecution();
 36
 2737        context.RegisterCompilationStartAction(compilationContext =>
 2738        {
 1539            var agents = new ConcurrentDictionary<string, (INamedTypeSymbol Symbol, Location Location)>(StringComparer.O
 1540            var topologyParticipants = new ConcurrentBag<string>();
 2741
 1542            compilationContext.RegisterSymbolAction(symbolContext =>
 1543            {
 18144                var typeSymbol = (INamedTypeSymbol)symbolContext.Symbol;
 18145                var fqn = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
 1546
 18147                Location? agentAttrLocation = null;
 18148                bool inTopology = false;
 1549
 70850                foreach (var attr in typeSymbol.GetAttributes())
 1551                {
 17352                    var attrName = attr.AttributeClass?.ToDisplayString();
 1553
 17354                    if (attrName == NeedlrAiAgentAttributeName)
 1555                    {
 2556                        agentAttrLocation = attr.ApplicationSyntaxReference?.SyntaxTree is { } tree
 2557                            ? Location.Create(tree, attr.ApplicationSyntaxReference.Span)
 2558                            : typeSymbol.Locations[0];
 1559                    }
 1560
 17361                    if (attrName == AgentGroupChatMemberAttributeName ||
 17362                        attrName == AgentSequenceMemberAttributeName ||
 17363                        attrName == AgentGraphEntryAttributeName ||
 17364                        attrName == AgentGraphNodeAttributeName ||
 17365                        attrName == AgentGraphReducerAttributeName)
 766                        inTopology = true;
 1567
 17368                    if (attrName == AgentGraphEdgeAttributeName)
 1569                    {
 170                        inTopology = true;
 1571
 1572                        // The edge target is also a topology participant
 173                        if (attr.ConstructorArguments.Length >= 2
 174                            && attr.ConstructorArguments[1].Kind == TypedConstantKind.Type
 175                            && attr.ConstructorArguments[1].Value is INamedTypeSymbol edgeTargetType)
 1576                        {
 177                            topologyParticipants.Add(
 178                                edgeTargetType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 1579                        }
 1580                    }
 1581
 17382                    if (attrName == AgentHandoffsToAttributeName)
 1583                    {
 484                        inTopology = true;
 1585
 1586                        // The handoff target is also a topology participant
 487                        if (attr.ConstructorArguments.Length >= 1
 488                            && attr.ConstructorArguments[0].Kind == TypedConstantKind.Type
 489                            && attr.ConstructorArguments[0].Value is INamedTypeSymbol targetType)
 1590                        {
 491                            topologyParticipants.Add(
 492                                targetType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 1593                        }
 1594                    }
 1595                }
 1596
 18197                if (agentAttrLocation is not null)
 2598                    agents[fqn] = (typeSymbol, agentAttrLocation);
 1599
 181100                if (inTopology)
 12101                    topologyParticipants.Add(fqn);
 196102            }, SymbolKind.NamedType);
 27103
 15104            compilationContext.RegisterCompilationEndAction(endContext =>
 15105            {
 15106                var participantSet = new HashSet<string>(topologyParticipants, StringComparer.Ordinal);
 15107
 80108                foreach (var kvp in agents)
 15109                {
 25110                    if (!participantSet.Contains(kvp.Key))
 15111                    {
 8112                        endContext.ReportDiagnostic(Diagnostic.Create(
 8113                            MafDiagnosticDescriptors.OrphanAgent,
 8114                            kvp.Value.Location,
 8115                            kvp.Value.Symbol.Name));
 15116                    }
 15117                }
 30118            });
 42119        });
 27120    }
 121}