< Summary

Information
Class: NexusLabs.Needlr.AgentFramework.Analyzers.AgentFunctionJsonStringParameterAnalyzer
Assembly: NexusLabs.Needlr.AgentFramework.Analyzers
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/AgentFunctionJsonStringParameterAnalyzer.cs
Line coverage
95%
Covered lines: 43
Uncovered lines: 2
Coverable lines: 45
Total lines: 103
Line coverage: 95.5%
Branch coverage
81%
Covered branches: 26
Total branches: 32
Branch coverage: 81.2%
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%
AnalyzeMethod(...)88.88%1818100%
LooksLikeJsonByName(...)100%22100%
LooksLikeJsonByDescription(...)66.66%121286.66%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.AgentFramework.Analyzers/AgentFunctionJsonStringParameterAnalyzer.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 hints when an <c>[AgentFunction]</c> <see cref="string"/> parameter is being
 10/// used to carry JSON (per its name suffix or <c>[Description]</c> text) and could instead be
 11/// typed as <c>System.Text.Json.JsonElement</c> for direct, typed access.
 12/// </summary>
 13/// <remarks>
 14/// <b>NDLRMAF030</b> (Info): a parameter is named <c>*Json</c>/<c>*_json</c> OR its description
 15/// mentions <c>"JSON array"</c>/<c>"JSON object"</c>, AND its declared type is <see cref="string"/>.
 16/// </remarks>
 17[DiagnosticAnalyzer(LanguageNames.CSharp)]
 18public sealed class AgentFunctionJsonStringParameterAnalyzer : DiagnosticAnalyzer
 19{
 20    private const string AgentFunctionAttributeName = "NexusLabs.Needlr.AgentFramework.AgentFunctionAttribute";
 21    private const string DescriptionAttributeName = "System.ComponentModel.DescriptionAttribute";
 22
 23    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
 24224        ImmutableArray.Create(MafDiagnosticDescriptors.AgentFunctionJsonStringParameter);
 25
 26    public override void Initialize(AnalysisContext context)
 27    {
 2628        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 2629        context.EnableConcurrentExecution();
 2630        context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
 2631    }
 32
 33    private static void AnalyzeMethod(SymbolAnalysisContext context)
 34    {
 41635        var method = (IMethodSymbol)context.Symbol;
 36
 41637        var hasAgentFunction = method.GetAttributes()
 43138            .Any(a => a.AttributeClass?.ToDisplayString() == AgentFunctionAttributeName);
 39
 41640        if (!hasAgentFunction)
 40141            return;
 42
 6843        foreach (var parameter in method.Parameters)
 44        {
 1945            if (parameter.Type.SpecialType != SpecialType.System_String)
 46                continue;
 47
 1848            var nameSignal = LooksLikeJsonByName(parameter.Name);
 1849            var descriptionSignal = LooksLikeJsonByDescription(parameter, out var descriptionExcerpt);
 50
 1851            if (!nameSignal && !descriptionSignal)
 52                continue;
 53
 1254            var reason = nameSignal && descriptionSignal
 1255                ? $"its name '{parameter.Name}' suggests JSON content and its description mentions {descriptionExcerpt}"
 1256                : nameSignal
 1257                    ? $"its name '{parameter.Name}' suggests JSON content"
 1258                    : $"its description mentions {descriptionExcerpt}";
 59
 1260            var location = parameter.Locations.FirstOrDefault() ?? method.Locations[0];
 61
 1262            context.ReportDiagnostic(Diagnostic.Create(
 1263                MafDiagnosticDescriptors.AgentFunctionJsonStringParameter,
 1264                location,
 1265                parameter.Name,
 1266                method.Name,
 1267                reason));
 68        }
 1569    }
 70
 71    private static bool LooksLikeJsonByName(string name) =>
 1872        name.EndsWith("Json", StringComparison.Ordinal) ||
 1873        name.EndsWith("_json", StringComparison.Ordinal);
 74
 75    private static bool LooksLikeJsonByDescription(IParameterSymbol parameter, out string excerpt)
 76    {
 1877        var descriptionAttr = parameter.GetAttributes()
 3678            .FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == DescriptionAttributeName);
 79
 1880        if (descriptionAttr is null
 1881            || descriptionAttr.ConstructorArguments.Length < 1
 1882            || descriptionAttr.ConstructorArguments[0].Value is not string text)
 83        {
 084            excerpt = string.Empty;
 085            return false;
 86        }
 87
 1888        if (text.IndexOf("JSON array", StringComparison.OrdinalIgnoreCase) >= 0)
 89        {
 690            excerpt = "\"JSON array\"";
 691            return true;
 92        }
 93
 1294        if (text.IndexOf("JSON object", StringComparison.OrdinalIgnoreCase) >= 0)
 95        {
 296            excerpt = "\"JSON object\"";
 297            return true;
 98        }
 99
 10100        excerpt = string.Empty;
 10101        return false;
 102    }
 103}