< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Analyzers.AgentFunctionGroupReferenceAnalyzer
Assembly: NexusLabs.Needlr.AgentFramework.Analyzers
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/AgentFunctionGroupReferenceAnalyzer.cs
Line coverage
100%
Covered lines: 65
Uncovered lines: 0
Coverable lines: 65
Total lines: 94
Line coverage: 100%
Branch coverage
90%
Covered branches: 29
Total branches: 32
Branch coverage: 90.6%
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(...)90.62%3232100%

File(s)

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

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Collections.Immutable;
 3using System.Linq;
 4
 5using Microsoft.CodeAnalysis;
 6using Microsoft.CodeAnalysis.Diagnostics;
 7
 8namespace NexusLabs.Needlr.AgentFramework.Analyzers;
 9
 10/// <summary>
 11/// Analyzer that validates <c>FunctionGroups</c> references in <c>[NeedlrAiAgent]</c> declarations.
 12/// </summary>
 13/// <remarks>
 14/// <b>NDLRMAF005</b> (Warning): An agent declares a <c>FunctionGroups</c> entry whose name does not
 15/// match any class decorated with <c>[AgentFunctionGroup]</c> in this compilation. The agent will
 16/// silently receive zero tools from that group at runtime.
 17/// </remarks>
 18[DiagnosticAnalyzer(LanguageNames.CSharp)]
 19public sealed class AgentFunctionGroupReferenceAnalyzer : DiagnosticAnalyzer
 20{
 21    private const string NeedlrAiAgentAttributeName = "NexusLabs.Needlr.AgentFramework.NeedlrAiAgentAttribute";
 22    private const string AgentFunctionGroupAttributeName = "NexusLabs.Needlr.AgentFramework.AgentFunctionGroupAttribute"
 23
 24    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
 16525        ImmutableArray.Create(MafDiagnosticDescriptors.UnresolvedFunctionGroupReference);
 26
 27    public override void Initialize(AnalysisContext context)
 28    {
 1929        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 1930        context.EnableConcurrentExecution();
 31
 1932        context.RegisterCompilationStartAction(compilationContext =>
 1933        {
 1134            var knownGroups = new ConcurrentBag<string>();
 1135            var references = new ConcurrentBag<(string AgentName, string GroupName, Location Location)>();
 1936
 1137            compilationContext.RegisterSymbolAction(symbolContext =>
 1138            {
 12039                var typeSymbol = (INamedTypeSymbol)symbolContext.Symbol;
 1140
 46041                foreach (var attr in typeSymbol.GetAttributes())
 1142                {
 11043                    var attrName = attr.AttributeClass?.ToDisplayString();
 1144
 11045                    if (attrName == AgentFunctionGroupAttributeName
 11046                        && attr.ConstructorArguments.Length >= 1
 11047                        && attr.ConstructorArguments[0].Value is string groupName
 11048                        && !string.IsNullOrWhiteSpace(groupName))
 1149                    {
 950                        knownGroups.Add(groupName);
 1151                    }
 1152
 11053                    if (attrName == NeedlrAiAgentAttributeName)
 1154                    {
 2555                        var functionGroupsArg = attr.NamedArguments.FirstOrDefault(a => a.Key == "FunctionGroups");
 1356                        if (functionGroupsArg.Key is null)
 1157                            continue;
 1158
 1259                        var arrayConstant = functionGroupsArg.Value;
 1260                        if (arrayConstant.Kind != TypedConstantKind.Array)
 1161                            continue;
 1162
 1263                        var attrLocation = attr.ApplicationSyntaxReference?.SyntaxTree is { } tree
 1264                            ? Location.Create(tree, attr.ApplicationSyntaxReference.Span)
 1265                            : typeSymbol.Locations[0];
 1166
 5267                        foreach (var item in arrayConstant.Values)
 1168                        {
 1469                            if (item.Value is string name && !string.IsNullOrWhiteSpace(name))
 1470                                references.Add((typeSymbol.Name, name, attrLocation));
 1171                        }
 1172                    }
 1173                }
 13174            }, SymbolKind.NamedType);
 1975
 1176            compilationContext.RegisterCompilationEndAction(endContext =>
 1177            {
 1178                var knownGroupSet = new HashSet<string>(knownGroups, StringComparer.Ordinal);
 1179
 5080                foreach (var (agentName, groupName, location) in references)
 1181                {
 1482                    if (!knownGroupSet.Contains(groupName))
 1183                    {
 884                        endContext.ReportDiagnostic(Diagnostic.Create(
 885                            MafDiagnosticDescriptors.UnresolvedFunctionGroupReference,
 886                            location,
 887                            agentName,
 888                            groupName));
 1189                    }
 1190                }
 2291            });
 3092        });
 1993    }
 94}