< Summary

Information
Class: NexusLabs.Needlr.Analyzers.DoNotAutoRegisterOnPluginAnalyzer
Assembly: NexusLabs.Needlr.Analyzers
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Analyzers/DoNotAutoRegisterOnPluginAnalyzer.cs
Line coverage
95%
Covered lines: 44
Uncovered lines: 2
Coverable lines: 46
Total lines: 109
Line coverage: 95.6%
Branch coverage
81%
Covered branches: 31
Total branches: 38
Branch coverage: 81.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_SupportedDiagnostics()100%11100%
Initialize(...)100%11100%
AnalyzeClassDeclaration(...)100%44100%
HasDoNotAutoRegisterDirectly(...)81.81%2222100%
ImplementsPluginInterface(...)75%141277.77%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Analyzers/DoNotAutoRegisterOnPluginAnalyzer.cs

#LineLine coverage
 1using System.Collections.Immutable;
 2
 3using Microsoft.CodeAnalysis;
 4using Microsoft.CodeAnalysis.CSharp;
 5using Microsoft.CodeAnalysis.CSharp.Syntax;
 6using Microsoft.CodeAnalysis.Diagnostics;
 7
 8namespace NexusLabs.Needlr.Analyzers;
 9
 10/// <summary>
 11/// Analyzer that warns when [DoNotAutoRegister] is applied directly to a class
 12/// that implements a Needlr plugin interface.
 13/// </summary>
 14[DiagnosticAnalyzer(LanguageNames.CSharp)]
 15public sealed class DoNotAutoRegisterOnPluginAnalyzer : DiagnosticAnalyzer
 16{
 117    private static readonly ImmutableHashSet<string> PluginInterfaceNames = ImmutableHashSet.Create(
 118        "IServiceCollectionPlugin",
 119        "IPostBuildServiceCollectionPlugin",
 120        "IWebApplicationPlugin",
 121        "IWebApplicationBuilderPlugin",
 122        "IHostApplicationBuilderPlugin");
 23
 24    private const string DoNotAutoRegisterAttributeName = "DoNotAutoRegisterAttribute";
 25    private const string DoNotAutoRegisterShortName = "DoNotAutoRegister";
 26
 27    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
 13328        ImmutableArray.Create(DiagnosticDescriptors.DoNotAutoRegisterOnPluginClass);
 29
 30    public override void Initialize(AnalysisContext context)
 31    {
 1332        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 1333        context.EnableConcurrentExecution();
 1334        context.RegisterSyntaxNodeAction(AnalyzeClassDeclaration, SyntaxKind.ClassDeclaration);
 1335    }
 36
 37    private static void AnalyzeClassDeclaration(SyntaxNodeAnalysisContext context)
 38    {
 2439        var classDeclaration = (ClassDeclarationSyntax)context.Node;
 40
 2441        if (!HasDoNotAutoRegisterDirectly(classDeclaration, context.SemanticModel, out var attributeLocation))
 1742            return;
 43
 744        if (!ImplementsPluginInterface(classDeclaration, context.SemanticModel))
 145            return;
 46
 647        var diagnostic = Diagnostic.Create(
 648            DiagnosticDescriptors.DoNotAutoRegisterOnPluginClass,
 649            attributeLocation,
 650            classDeclaration.Identifier.Text);
 51
 652        context.ReportDiagnostic(diagnostic);
 653    }
 54
 55    private static bool HasDoNotAutoRegisterDirectly(
 56        ClassDeclarationSyntax classDeclaration,
 57        SemanticModel semanticModel,
 58        out Location? attributeLocation)
 59    {
 2460        attributeLocation = null;
 8761        foreach (var attributeList in classDeclaration.AttributeLists)
 62        {
 8563            foreach (var attribute in attributeList.Attributes)
 64            {
 2365                var name = attribute.Name.ToString();
 2366                if (name == DoNotAutoRegisterShortName || name == DoNotAutoRegisterAttributeName ||
 2367                    name.EndsWith("." + DoNotAutoRegisterShortName) || name.EndsWith("." + DoNotAutoRegisterAttributeNam
 68                {
 769                    var symbolInfo = semanticModel.GetSymbolInfo(attribute);
 770                    var attributeClass = symbolInfo.Symbol?.ContainingType
 771                        ?? symbolInfo.CandidateSymbols.FirstOrDefault()?.ContainingType;
 72
 773                    if (attributeClass != null)
 74                    {
 775                        var fullName = attributeClass.ToDisplayString();
 776                        if (fullName != "NexusLabs.Needlr.DoNotAutoRegisterAttribute")
 77                            continue;
 78                    }
 79
 780                    attributeLocation = attributeList.GetLocation();
 781                    return true;
 82                }
 83            }
 84        }
 1785        return false;
 86    }
 87
 88    private static bool ImplementsPluginInterface(
 89        ClassDeclarationSyntax classDeclaration,
 90        SemanticModel semanticModel)
 91    {
 792        if (classDeclaration.BaseList == null)
 193            return false;
 94
 695        if (semanticModel.GetDeclaredSymbol(classDeclaration) is not INamedTypeSymbol classSymbol)
 096            return false;
 97
 1898        foreach (var iface in classSymbol.AllInterfaces)
 99        {
 6100            if (PluginInterfaceNames.Contains(iface.Name) &&
 6101                iface.ContainingNamespace?.ToString() == "NexusLabs.Needlr")
 102            {
 6103                return true;
 104            }
 105        }
 106
 0107        return false;
 108    }
 109}