< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Analyzers.AgentGraphTopologyAnalyzer
Assembly: NexusLabs.Needlr.AgentFramework.Analyzers
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/AgentGraphTopologyAnalyzer.cs
Line coverage
100%
Covered lines: 43
Uncovered lines: 0
Coverable lines: 43
Total lines: 107
Line coverage: 100%
Branch coverage
80%
Covered branches: 24
Total branches: 30
Branch coverage: 80%
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(...)80%3030100%

File(s)

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

#LineLine coverage
 1using System.Collections.Immutable;
 2using System.Linq;
 3
 4using Microsoft.CodeAnalysis;
 5using Microsoft.CodeAnalysis.Diagnostics;
 6
 7namespace NexusLabs.Needlr.AgentFramework.Analyzers;
 8
 9/// <summary>
 10/// Analyzer that validates graph edge and entry point declarations reference declared agents.
 11/// </summary>
 12/// <remarks>
 13/// <para>
 14/// <b>NDLRMAF019</b> (Error): An <c>[AgentGraphEdge]</c> target type is not decorated with
 15/// <c>[NeedlrAiAgent]</c>.
 16/// </para>
 17/// <para>
 18/// <b>NDLRMAF020</b> (Warning): A class has <c>[AgentGraphEdge]</c> but is not itself decorated
 19/// with <c>[NeedlrAiAgent]</c>.
 20/// </para>
 21/// <para>
 22/// <b>NDLRMAF021</b> (Warning): A class has <c>[AgentGraphEntry]</c> but is not itself decorated
 23/// with <c>[NeedlrAiAgent]</c>.
 24/// </para>
 25/// </remarks>
 26[DiagnosticAnalyzer(LanguageNames.CSharp)]
 27public sealed class AgentGraphTopologyAnalyzer : DiagnosticAnalyzer
 28{
 29    private const string AgentGraphEdgeAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphEdgeAttribute";
 30    private const string AgentGraphEntryAttributeName = "NexusLabs.Needlr.AgentFramework.AgentGraphEntryAttribute";
 31    private const string NeedlrAiAgentAttributeName = "NexusLabs.Needlr.AgentFramework.NeedlrAiAgentAttribute";
 32
 33    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
 18534        ImmutableArray.Create(
 18535            MafDiagnosticDescriptors.GraphEdgeTargetNotAgent,
 18536            MafDiagnosticDescriptors.GraphEdgeSourceNotAgent,
 18537            MafDiagnosticDescriptors.GraphEntryPointNotAgent);
 38
 39    public override void Initialize(AnalysisContext context)
 40    {
 1841        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 1842        context.EnableConcurrentExecution();
 43
 1844        context.RegisterSymbolAction(AnalyzeNamedType, SymbolKind.NamedType);
 1845    }
 46
 47    private static void AnalyzeNamedType(SymbolAnalysisContext context)
 48    {
 17349        var typeSymbol = (INamedTypeSymbol)context.Symbol;
 17350        var attributes = typeSymbol.GetAttributes();
 51
 17352        var hasNeedlrAiAgent = attributes
 31953            .Any(a => a.AttributeClass?.ToDisplayString() == NeedlrAiAgentAttributeName);
 54
 17355        var graphEdgeAttrs = attributes
 15056            .Where(a => a.AttributeClass?.ToDisplayString() == AgentGraphEdgeAttributeName)
 17357            .ToImmutableArray();
 58
 17359        var graphEntryAttrs = attributes
 15060            .Where(a => a.AttributeClass?.ToDisplayString() == AgentGraphEntryAttributeName)
 17361            .ToImmutableArray();
 62
 63        // NDLRMAF020: source class has [AgentGraphEdge] but no [NeedlrAiAgent]
 17364        if (!graphEdgeAttrs.IsEmpty && !hasNeedlrAiAgent)
 65        {
 466            context.ReportDiagnostic(Diagnostic.Create(
 467                MafDiagnosticDescriptors.GraphEdgeSourceNotAgent,
 468                typeSymbol.Locations[0],
 469                typeSymbol.Name));
 70        }
 71
 72        // NDLRMAF021: source class has [AgentGraphEntry] but no [NeedlrAiAgent]
 17373        if (!graphEntryAttrs.IsEmpty && !hasNeedlrAiAgent)
 74        {
 275            context.ReportDiagnostic(Diagnostic.Create(
 276                MafDiagnosticDescriptors.GraphEntryPointNotAgent,
 277                typeSymbol.Locations[0],
 278                typeSymbol.Name));
 79        }
 80
 81        // NDLRMAF019: each edge target must have [NeedlrAiAgent]
 36082        foreach (var attr in graphEdgeAttrs)
 83        {
 784            if (attr.ConstructorArguments.Length < 2)
 85                continue;
 86
 787            var targetTypeArg = attr.ConstructorArguments[1];
 788            if (targetTypeArg.Kind != TypedConstantKind.Type || targetTypeArg.Value is not INamedTypeSymbol targetType)
 89                continue;
 90
 791            var targetHasNeedlrAiAgent = targetType.GetAttributes()
 1092                .Any(a => a.AttributeClass?.ToDisplayString() == NeedlrAiAgentAttributeName);
 93
 794            if (!targetHasNeedlrAiAgent)
 95            {
 496                var location = attr.ApplicationSyntaxReference?.SyntaxTree is { } tree
 497                    ? Location.Create(tree, attr.ApplicationSyntaxReference.Span)
 498                    : typeSymbol.Locations[0];
 99
 4100                context.ReportDiagnostic(Diagnostic.Create(
 4101                    MafDiagnosticDescriptors.GraphEdgeTargetNotAgent,
 4102                    location,
 4103                    targetType.Name));
 104            }
 105        }
 173106    }
 107}