< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Analyzers.AgentTopologyAnalyzer
Assembly: NexusLabs.Needlr.AgentFramework.Analyzers
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/AgentTopologyAnalyzer.cs
Line coverage
100%
Covered lines: 35
Uncovered lines: 0
Coverable lines: 35
Total lines: 90
Line coverage: 100%
Branch coverage
79%
Covered branches: 19
Total branches: 24
Branch coverage: 79.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(...)100%11100%
AnalyzeNamedType(...)79.16%2424100%

File(s)

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

#LineLine coverage
 1using System.Collections.Immutable;
 2
 3using Microsoft.CodeAnalysis;
 4using Microsoft.CodeAnalysis.Diagnostics;
 5
 6namespace NexusLabs.Needlr.AgentFramework.Analyzers;
 7
 8/// <summary>
 9/// Analyzer that validates <c>[AgentHandoffsTo]</c> topology declarations.
 10/// </summary>
 11/// <remarks>
 12/// <para>
 13/// <b>NDLRMAF001</b> (Error): The target type referenced by <c>[AgentHandoffsTo(typeof(X))]</c> is not
 14/// decorated with <c>[NeedlrAiAgent]</c>. Handoff targets must be registered agent types.
 15/// </para>
 16/// <para>
 17/// <b>NDLRMAF003</b> (Warning): The class carrying <c>[AgentHandoffsTo]</c> is not itself decorated with
 18/// <c>[NeedlrAiAgent]</c>. The initial agent in a handoff workflow must be a declared agent.
 19/// </para>
 20/// </remarks>
 21[DiagnosticAnalyzer(LanguageNames.CSharp)]
 22public sealed class AgentTopologyAnalyzer : DiagnosticAnalyzer
 23{
 24    private const string AgentHandoffsToAttributeName = "NexusLabs.Needlr.AgentFramework.AgentHandoffsToAttribute";
 25    private const string NeedlrAiAgentAttributeName = "NexusLabs.Needlr.AgentFramework.NeedlrAiAgentAttribute";
 26
 27    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
 28128        ImmutableArray.Create(
 28129            MafDiagnosticDescriptors.HandoffsToTargetNotNeedlrAgent,
 28130            MafDiagnosticDescriptors.HandoffsToSourceNotNeedlrAgent);
 31
 32    public override void Initialize(AnalysisContext context)
 33    {
 4234        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 4235        context.EnableConcurrentExecution();
 36
 4237        context.RegisterSymbolAction(AnalyzeNamedType, SymbolKind.NamedType);
 4238    }
 39
 40    private static void AnalyzeNamedType(SymbolAnalysisContext context)
 41    {
 35342        var typeSymbol = (INamedTypeSymbol)context.Symbol;
 43
 35344        var handoffsToAttrs = typeSymbol.GetAttributes()
 33245            .Where(a => a.AttributeClass?.ToDisplayString() == AgentHandoffsToAttributeName)
 35346            .ToImmutableArray();
 47
 35348        if (handoffsToAttrs.IsEmpty)
 32249            return;
 50
 51        // NDLRMAF003: source class lacks [NeedlrAiAgent]
 3152        var hasNeedlrAiAgent = typeSymbol.GetAttributes()
 6253            .Any(a => a.AttributeClass?.ToDisplayString() == NeedlrAiAgentAttributeName);
 54
 3155        if (!hasNeedlrAiAgent)
 56        {
 1057            context.ReportDiagnostic(Diagnostic.Create(
 1058                MafDiagnosticDescriptors.HandoffsToSourceNotNeedlrAgent,
 1059                typeSymbol.Locations[0],
 1060                typeSymbol.Name));
 61        }
 62
 63        // NDLRMAF001: each target type must have [NeedlrAiAgent]
 12864        foreach (var attr in handoffsToAttrs)
 65        {
 3366            if (attr.ConstructorArguments.Length < 1)
 67                continue;
 68
 3369            var typeArg = attr.ConstructorArguments[0];
 3370            if (typeArg.Kind != TypedConstantKind.Type || typeArg.Value is not INamedTypeSymbol targetType)
 71                continue;
 72
 3373            var targetHasNeedlrAiAgent = targetType.GetAttributes()
 5474                .Any(a => a.AttributeClass?.ToDisplayString() == NeedlrAiAgentAttributeName);
 75
 3376            if (!targetHasNeedlrAiAgent)
 77            {
 78                // Report on the attribute usage location when available, otherwise fall back to the class
 1279                var location = attr.ApplicationSyntaxReference?.SyntaxTree is { } tree
 1280                    ? Location.Create(tree, attr.ApplicationSyntaxReference.Span)
 1281                    : typeSymbol.Locations[0];
 82
 1283                context.ReportDiagnostic(Diagnostic.Create(
 1284                    MafDiagnosticDescriptors.HandoffsToTargetNotNeedlrAgent,
 1285                    location,
 1286                    targetType.Name));
 87            }
 88        }
 3189    }
 90}