< Summary

Information
Class: NexusLabs.Needlr.Generators.TypeRegistryGenerator
Assembly: NexusLabs.Needlr.Generators
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/TypeRegistryGenerator.cs
Line coverage
94%
Covered lines: 490
Uncovered lines: 31
Coverable lines: 521
Total lines: 860
Line coverage: 94%
Branch coverage
85%
Covered branches: 233
Total branches: 274
Branch coverage: 85%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Initialize(...)96.55%585899.47%
GetBreadcrumbLevel(...)100%88100%
GetProjectDirectory(...)50%4475%
GetDiagnosticOptions(...)100%11100%
ShouldExportGraph(...)100%44100%
IsAotProject(...)100%88100%
GetAttributeInfoFromCompilation(...)79.16%382470.83%
DiscoverTypes(...)96.42%2828100%
CollectTypesFromAssembly(...)76.66%14412088.09%
GenerateTypeRegistrySource(...)100%1010100%
GenerateRegisterOptionsMethod(...)70%101089.47%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Generators/TypeRegistryGenerator.cs

#LineLine coverage
 1using Microsoft.CodeAnalysis;
 2using Microsoft.CodeAnalysis.Text;
 3using NexusLabs.Needlr.Generators.Helpers;
 4using NexusLabs.Needlr.Generators.Models;
 5using System.Text;
 6
 7namespace NexusLabs.Needlr.Generators;
 8
 9/// <summary>
 10/// Incremental source generator that produces a compile-time type registry
 11/// for dependency injection, eliminating runtime reflection.
 12/// </summary>
 13[Generator(LanguageNames.CSharp)]
 14public sealed class TypeRegistryGenerator : IIncrementalGenerator
 15{
 16    private const string GenerateTypeRegistryAttributeName = "NexusLabs.Needlr.Generators.GenerateTypeRegistryAttribute"
 17
 18    public void Initialize(IncrementalGeneratorInitializationContext context)
 19    {
 20        // Combine compilation with analyzer config options to read MSBuild properties
 45821        var compilationAndOptions = context.CompilationProvider
 45822            .Combine(context.AnalyzerConfigOptionsProvider);
 23
 24        // ForAttributeWithMetadataName doesn't work for assembly-level attributes.
 25        // Instead, we register directly on the compilation provider and check
 26        // compilation.Assembly.GetAttributes() for [GenerateTypeRegistry].
 45827        context.RegisterSourceOutput(compilationAndOptions, static (spc, source) =>
 45828        {
 45829            var (compilation, configOptions) = source;
 45830
 45831            var attributeInfo = GetAttributeInfoFromCompilation(compilation);
 45832            if (attributeInfo == null)
 033                return;
 45834
 45835            var info = attributeInfo.Value;
 45836            var assemblyName = compilation.AssemblyName ?? "Generated";
 45837
 45838            // Read breadcrumb level from MSBuild property
 45839            var breadcrumbLevel = GetBreadcrumbLevel(configOptions);
 45840            var projectDirectory = GetProjectDirectory(configOptions);
 45841            var breadcrumbs = new BreadcrumbWriter(breadcrumbLevel);
 45842
 45843            // Check if this is an AOT project
 45844            var isAotProject = IsAotProject(configOptions);
 45845
 45846            var discoveryResult = DiscoverTypes(
 45847                compilation,
 45848                info.NamespacePrefixes,
 45849                info.ExcludeNamespacePrefixes,
 45850                info.IncludeSelf);
 45851
 45852            // Discover referenced assemblies with [GenerateTypeRegistry] for forced loading.
 45853            // Done early so the empty-result check below can include this in its decision.
 45854            // Note: Order of force-loading doesn't matter; ordering is applied at service registration time
 45855            var referencedAssemblies = AssemblyDiscoveryHelper.DiscoverReferencedAssembliesWithTypeRegistry(compilation)
 256                .OrderBy(a => a, StringComparer.OrdinalIgnoreCase)
 45857                .ToList();
 45858
 45859            // If nothing was discovered — no injectable types, factories, providers, options,
 45860            // interceptors, hosted services, plugins, no referenced assemblies to force-load,
 45861            // no inaccessible type errors, and no missing TypeRegistry warnings —
 45862            // emit nothing. This avoids compile errors in projects that don't reference Needlr
 45863            // injection packages (e.g. a documentation-only project): the generated bootstrap code
 45864            // references types from those packages and would fail to build without them.
 45865            if (discoveryResult.InjectableTypes.Count == 0 &&
 45866                discoveryResult.PluginTypes.Count == 0 &&
 45867                discoveryResult.Decorators.Count == 0 &&
 45868                discoveryResult.InterceptedServices.Count == 0 &&
 45869                discoveryResult.Factories.Count == 0 &&
 45870                discoveryResult.Options.Count == 0 &&
 45871                discoveryResult.HttpClients.Count == 0 &&
 45872                discoveryResult.HostedServices.Count == 0 &&
 45873                discoveryResult.Providers.Count == 0 &&
 45874                discoveryResult.InaccessibleTypes.Count == 0 &&
 45875                discoveryResult.MissingTypeRegistryPlugins.Count == 0 &&
 45876                referencedAssemblies.Count == 0)
 45877            {
 378                return;
 45879            }
 45880
 45881            // Report errors for inaccessible internal types in referenced assemblies
 554882            foreach (var inaccessibleType in discoveryResult.InaccessibleTypes)
 45883            {
 231984                spc.ReportDiagnostic(Diagnostic.Create(
 231985                    DiagnosticDescriptors.InaccessibleInternalType,
 231986                    Location.None,
 231987                    inaccessibleType.TypeName,
 231988                    inaccessibleType.AssemblyName));
 45889            }
 45890
 45891            // Report errors for referenced assemblies with internal plugin types but no [GenerateTypeRegistry]
 91292            foreach (var missingPlugin in discoveryResult.MissingTypeRegistryPlugins)
 45893            {
 194                spc.ReportDiagnostic(Diagnostic.Create(
 195                    DiagnosticDescriptors.MissingGenerateTypeRegistryAttribute,
 196                    Location.None,
 197                    missingPlugin.AssemblyName,
 198                    missingPlugin.TypeName));
 45899            }
 458100
 458101            // NDLRGEN020: Previously reported error if [Options] used in AOT project
 458102            // Now removed for parity - we generate best-effort code and let unsupported
 458103            // types fail at runtime (matching non-AOT ConfigurationBinder behavior)
 458104
 458105            // NDLRGEN021: Report warning for non-partial positional records
 1079106            foreach (var opt in discoveryResult.Options.Where(o => o.IsNonPartialPositionalRecord))
 458107            {
 2108                spc.ReportDiagnostic(Diagnostic.Create(
 2109                    DiagnosticDescriptors.PositionalRecordMustBePartial,
 2110                    Location.None,
 2111                    opt.TypeName));
 458112            }
 458113
 458114            // NDLRGEN022: Detect disposable captive dependencies using inferred lifetimes
 455115            CaptiveDependencyAnalyzer.ReportDisposableCaptiveDependencies(spc, discoveryResult);
 458116
 455117            var sourceText = GenerateTypeRegistrySource(discoveryResult, assemblyName, breadcrumbs, projectDirectory, is
 455118            spc.AddSource("TypeRegistry.g.cs", SourceText.From(sourceText, Encoding.UTF8));
 458119
 455120            var bootstrapText = CodeGen.BootstrapCodeGenerator.GenerateModuleInitializerBootstrapSource(assemblyName, re
 455121            spc.AddSource("NeedlrSourceGenBootstrap.g.cs", SourceText.From(bootstrapText, Encoding.UTF8));
 458122
 458123            // Generate interceptor proxy classes if any were discovered
 455124            if (discoveryResult.InterceptedServices.Count > 0)
 458125            {
 14126                var interceptorProxiesText = CodeGen.InterceptorCodeGenerator.GenerateInterceptorProxiesSource(discovery
 14127                spc.AddSource("InterceptorProxies.g.cs", SourceText.From(interceptorProxiesText, Encoding.UTF8));
 458128            }
 458129
 458130            // Generate factory classes if any were discovered
 455131            if (discoveryResult.Factories.Count > 0)
 458132            {
 24133                var factoriesText = CodeGen.FactoryCodeGenerator.GenerateFactoriesSource(discoveryResult.Factories, asse
 24134                spc.AddSource("Factories.g.cs", SourceText.From(factoriesText, Encoding.UTF8));
 458135            }
 458136
 458137            // Generate provider classes if any were discovered
 455138            if (discoveryResult.Providers.Count > 0)
 458139            {
 458140                // Interface-based providers go in the Generated namespace
 35141                var interfaceProviders = discoveryResult.Providers.Where(p => p.IsInterface).ToList();
 17142                if (interfaceProviders.Count > 0)
 458143                {
 11144                    var providersText = CodeGen.ProviderCodeGenerator.GenerateProvidersSource(interfaceProviders, assemb
 11145                    spc.AddSource("Providers.g.cs", SourceText.From(providersText, Encoding.UTF8));
 458146                }
 458147
 458148                // Shorthand class providers need to be generated in their original namespace
 35149                var classProviders = discoveryResult.Providers.Where(p => !p.IsInterface && p.IsPartial).ToList();
 46150                foreach (var provider in classProviders)
 458151                {
 6152                    var providerText = CodeGen.ProviderCodeGenerator.GenerateShorthandProviderSource(provider, assemblyN
 6153                    spc.AddSource($"Provider.{provider.SimpleTypeName}.g.cs", SourceText.From(providerText, Encoding.UTF
 458154                }
 458155            }
 458156
 458157            // Generate options validator classes if any have validation methods
 620158            var optionsWithValidators = discoveryResult.Options.Where(o => o.HasValidatorMethod).ToList();
 455159            if (optionsWithValidators.Count > 0)
 458160            {
 18161                var validatorsText = CodeGen.OptionsCodeGenerator.GenerateOptionsValidatorsSource(optionsWithValidators,
 18162                spc.AddSource("OptionsValidators.g.cs", SourceText.From(validatorsText, Encoding.UTF8));
 458163            }
 458164
 458165            // Generate DataAnnotations validator classes if any have DataAnnotation attributes
 620166            var optionsWithDataAnnotations = discoveryResult.Options.Where(o => o.HasDataAnnotations).ToList();
 455167            if (optionsWithDataAnnotations.Count > 0)
 458168            {
 18169                var dataAnnotationsValidatorsText = CodeGen.OptionsCodeGenerator.GenerateDataAnnotationsValidatorsSource
 18170                spc.AddSource("OptionsDataAnnotationsValidators.g.cs", SourceText.From(dataAnnotationsValidatorsText, En
 458171            }
 458172
 458173            // Generate parameterless constructors for partial positional records with [Options]
 620174            var optionsNeedingConstructors = discoveryResult.Options.Where(o => o.NeedsGeneratedConstructor).ToList();
 455175            if (optionsNeedingConstructors.Count > 0)
 458176            {
 7177                var constructorsText = CodeGen.OptionsCodeGenerator.GeneratePositionalRecordConstructorsSource(optionsNe
 7178                spc.AddSource("OptionsConstructors.g.cs", SourceText.From(constructorsText, Encoding.UTF8));
 458179            }
 458180
 458181            // Generate ServiceCatalog for runtime introspection
 455182            var catalogText = CodeGen.ServiceCatalogCodeGenerator.GenerateServiceCatalogSource(discoveryResult, assembly
 455183            spc.AddSource("ServiceCatalog.g.cs", SourceText.From(catalogText, Encoding.UTF8));
 458184
 458185            // Generate diagnostic output files if configured
 455186            var diagnosticOptions = GetDiagnosticOptions(configOptions);
 455187            if (diagnosticOptions.Enabled)
 458188            {
 95189                var referencedAssemblyTypes = AssemblyDiscoveryHelper.DiscoverReferencedAssemblyTypesForDiagnostics(comp
 95190                var diagnosticsText = DiagnosticsGenerator.GenerateDiagnosticsSource(discoveryResult, assemblyName, proj
 95191                spc.AddSource("NeedlrDiagnostics.g.cs", SourceText.From(diagnosticsText, Encoding.UTF8));
 458192            }
 458193
 458194            // Generate IDE graph export if configured
 455195            if (ShouldExportGraph(configOptions))
 458196            {
 458197                // Discover types from referenced assemblies with [GenerateTypeRegistry] for graph inclusion
 4198                var referencedAssemblyTypesForGraph = AssemblyDiscoveryHelper.DiscoverReferencedAssemblyTypesForGraph(co
 458199
 4200                var graphJson = Export.GraphExporter.GenerateGraphJson(
 4201                    discoveryResult,
 4202                    assemblyName,
 4203                    projectDirectory,
 4204                    diagnostics: null,
 4205                    referencedAssemblyTypes: referencedAssemblyTypesForGraph);
 458206
 458207                // Embed graph as a comment in a generated file so it's accessible
 458208                // The actual JSON is written to obj folder via the generated code
 4209                var graphSourceText = Export.GraphExporter.GenerateGraphExportSource(graphJson, assemblyName, breadcrumb
 4210                spc.AddSource("NeedlrGraph.g.cs", SourceText.From(graphSourceText, Encoding.UTF8));
 458211            }
 913212        });
 458213    }
 214
 215    private static BreadcrumbLevel GetBreadcrumbLevel(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider c
 216    {
 458217        if (configOptions.GlobalOptions.TryGetValue("build_property.NeedlrBreadcrumbLevel", out var levelStr) &&
 458218            !string.IsNullOrWhiteSpace(levelStr))
 219        {
 259220            if (levelStr.Equals("None", StringComparison.OrdinalIgnoreCase))
 17221                return BreadcrumbLevel.None;
 242222            if (levelStr.Equals("Verbose", StringComparison.OrdinalIgnoreCase))
 28223                return BreadcrumbLevel.Verbose;
 224        }
 225
 226        // Default to Minimal
 413227        return BreadcrumbLevel.Minimal;
 228    }
 229
 230    private static string? GetProjectDirectory(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider configOp
 231    {
 232        // Try to get the project directory from MSBuild properties
 458233        if (configOptions.GlobalOptions.TryGetValue("build_property.ProjectDir", out var projectDir) &&
 458234            !string.IsNullOrWhiteSpace(projectDir))
 235        {
 0236            return projectDir.TrimEnd('/', '\\');
 237        }
 238
 458239        return null;
 240    }
 241
 242    private static DiagnosticOptions GetDiagnosticOptions(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvid
 243    {
 455244        configOptions.GlobalOptions.TryGetValue("build_property.NeedlrDiagnostics", out var enabled);
 455245        configOptions.GlobalOptions.TryGetValue("build_property.NeedlrDiagnosticsPath", out var outputPath);
 455246        configOptions.GlobalOptions.TryGetValue("build_property.NeedlrDiagnosticsFilter", out var filter);
 247
 455248        return DiagnosticOptions.Parse(enabled, outputPath, filter);
 249    }
 250
 251    /// <summary>
 252    /// Checks if the IDE graph export is enabled.
 253    /// </summary>
 254    private static bool ShouldExportGraph(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider configOptions
 255    {
 256        // Export graph is disabled by default
 257        // Enable with NeedlrExportGraph=true in project file
 455258        if (configOptions.GlobalOptions.TryGetValue("build_property.NeedlrExportGraph", out var exportGraph) &&
 455259            exportGraph.Equals("true", StringComparison.OrdinalIgnoreCase))
 260        {
 4261            return true;
 262        }
 451263        return false;
 264    }
 265
 266    /// <summary>
 267    /// Checks if the project is configured for AOT compilation.
 268    /// Returns true if either PublishAot or IsAotCompatible is set to true.
 269    /// </summary>
 270    private static bool IsAotProject(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider configOptions)
 271    {
 458272        if (configOptions.GlobalOptions.TryGetValue("build_property.PublishAot", out var publishAot) &&
 458273            publishAot.Equals("true", StringComparison.OrdinalIgnoreCase))
 274        {
 79275            return true;
 276        }
 277
 379278        if (configOptions.GlobalOptions.TryGetValue("build_property.IsAotCompatible", out var isAotCompatible) &&
 379279            isAotCompatible.Equals("true", StringComparison.OrdinalIgnoreCase))
 280        {
 1281            return true;
 282        }
 283
 378284        return false;
 285    }
 286
 287    private static AttributeInfo? GetAttributeInfoFromCompilation(Compilation compilation)
 288    {
 289        // Get assembly-level attributes directly from the compilation
 1374290        foreach (var attribute in compilation.Assembly.GetAttributes())
 291        {
 458292            var attrClassName = attribute.AttributeClass?.ToDisplayString();
 293
 294            // Check if this is our attribute (various name format possibilities)
 458295            if (attrClassName != GenerateTypeRegistryAttributeName)
 296                continue;
 297
 458298            string[]? namespacePrefixes = null;
 458299            string[]? excludeNamespacePrefixes = null;
 458300            var includeSelf = true;
 301
 1080302            foreach (var namedArg in attribute.NamedArguments)
 303            {
 82304                switch (namedArg.Key)
 305                {
 306                    case "IncludeNamespacePrefixes":
 72307                        if (!namedArg.Value.IsNull && namedArg.Value.Values.Length > 0)
 308                        {
 72309                            namespacePrefixes = namedArg.Value.Values
 73310                                .Where(v => v.Value is string)
 73311                                .Select(v => (string)v.Value!)
 72312                                .ToArray();
 313                        }
 72314                        break;
 315
 316                    case "ExcludeNamespacePrefixes":
 0317                        if (!namedArg.Value.IsNull && namedArg.Value.Values.Length > 0)
 318                        {
 0319                            excludeNamespacePrefixes = namedArg.Value.Values
 0320                                .Where(v => v.Value is string)
 0321                                .Select(v => (string)v.Value!)
 0322                                .ToArray();
 323                        }
 0324                        break;
 325
 326                    case "IncludeSelf":
 10327                        if (namedArg.Value.Value is bool selfValue)
 328                        {
 10329                            includeSelf = selfValue;
 330                        }
 331                        break;
 332                }
 333            }
 334
 458335            return new AttributeInfo(namespacePrefixes, excludeNamespacePrefixes, includeSelf);
 336        }
 337
 0338        return null;
 339    }
 340
 341    private static DiscoveryResult DiscoverTypes(
 342        Compilation compilation,
 343        string[]? namespacePrefixes,
 344        string[]? excludeNamespacePrefixes,
 345        bool includeSelf)
 346    {
 458347        var injectableTypes = new List<DiscoveredType>();
 458348        var pluginTypes = new List<DiscoveredPlugin>();
 458349        var decorators = new List<DiscoveredDecorator>();
 458350        var openDecorators = new List<DiscoveredOpenDecorator>();
 458351        var interceptedServices = new List<DiscoveredInterceptedService>();
 458352        var factories = new List<DiscoveredFactory>();
 458353        var options = new List<DiscoveredOptions>();
 458354        var hostedServices = new List<DiscoveredHostedService>();
 458355        var providers = new List<DiscoveredProvider>();
 458356        var httpClients = new List<DiscoveredHttpClient>();
 458357        var inaccessibleTypes = new List<InaccessibleType>();
 458358        var prefixList = namespacePrefixes?.ToList();
 458359        var excludePrefixList = excludeNamespacePrefixes?.ToList();
 360
 361        // Compute the generated namespace for the current assembly
 458362        var currentAssemblyName = compilation.Assembly.Name;
 458363        var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(currentAssemblyName);
 458364        var generatedNamespace = $"{safeAssemblyName}.Generated";
 365
 366        // Collect types from the current compilation if includeSelf is true
 458367        if (includeSelf)
 368        {
 457369            CollectTypesFromAssembly(compilation.Assembly, prefixList, excludePrefixList, injectableTypes, pluginTypes, 
 370        }
 371
 372        // Collect types from all referenced assemblies
 155626373        foreach (var reference in compilation.References)
 374        {
 77355375            if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assemblySymbol)
 376            {
 377                // Skip assemblies that already have [GenerateTypeRegistry] — those assemblies
 378                // register their own types at runtime via their own TypeRegistry and cascade
 379                // loading. Scanning them here would trigger false NDLRGEN001 errors for their
 380                // internal types.
 77168381                if (TypeDiscoveryHelper.HasGenerateTypeRegistryAttribute(assemblySymbol))
 382                    continue;
 383
 384                // For referenced assemblies, they use their own generated namespace
 77150385                var refSafeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblySymbol.Name);
 77150386                var refGeneratedNamespace = $"{refSafeAssemblyName}.Generated";
 77150387                CollectTypesFromAssembly(assemblySymbol, prefixList, excludePrefixList, injectableTypes, pluginTypes, de
 388            }
 389        }
 390
 391        // Expand open generic decorators into closed decorator registrations
 458392        if (openDecorators.Count > 0)
 393        {
 6394            CodeGen.DecoratorsCodeGenerator.ExpandOpenDecorators(injectableTypes, openDecorators, decorators);
 395        }
 396
 397        // Filter out nested options types (types used as properties in other options types)
 458398        if (options.Count > 1)
 399        {
 19400            options = OptionsDiscoveryHelper.FilterNestedOptions(options, compilation);
 401        }
 402
 403        // Check for referenced assemblies with internal plugin types but no [GenerateTypeRegistry]
 458404        var missingTypeRegistryPlugins = new List<MissingTypeRegistryPlugin>();
 155626405        foreach (var reference in compilation.References)
 406        {
 77355407            if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assemblySymbol)
 408            {
 409                // Skip assemblies that already have [GenerateTypeRegistry]
 77168410                if (TypeDiscoveryHelper.HasGenerateTypeRegistryAttribute(assemblySymbol))
 411                    continue;
 412
 413                // Look for internal types that implement Needlr plugin interfaces
 3780606414                foreach (var typeSymbol in TypeDiscoveryHelper.GetAllTypes(assemblySymbol.GlobalNamespace))
 415                {
 1813153416                    if (!TypeDiscoveryHelper.IsInternalOrLessAccessible(typeSymbol))
 417                        continue;
 418
 86266419                    if (!TypeDiscoveryHelper.ImplementsNeedlrPluginInterface(typeSymbol))
 420                        continue;
 421
 422                    // This is an internal plugin type in an assembly without [GenerateTypeRegistry]
 1423                    var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 1424                    missingTypeRegistryPlugins.Add(new MissingTypeRegistryPlugin(typeName, assemblySymbol.Name));
 425                }
 426            }
 427        }
 428
 458429        return new DiscoveryResult(injectableTypes, pluginTypes, decorators, inaccessibleTypes, missingTypeRegistryPlugi
 430    }
 431
 432    private static void CollectTypesFromAssembly(
 433        IAssemblySymbol assembly,
 434        IReadOnlyList<string>? namespacePrefixes,
 435        IReadOnlyList<string>? excludeNamespacePrefixes,
 436        List<DiscoveredType> injectableTypes,
 437        List<DiscoveredPlugin> pluginTypes,
 438        List<DiscoveredDecorator> decorators,
 439        List<DiscoveredOpenDecorator> openDecorators,
 440        List<DiscoveredInterceptedService> interceptedServices,
 441        List<DiscoveredFactory> factories,
 442        List<DiscoveredOptions> options,
 443        List<DiscoveredHostedService> hostedServices,
 444        List<DiscoveredProvider> providers,
 445        List<DiscoveredHttpClient> httpClients,
 446        List<InaccessibleType> inaccessibleTypes,
 447        Compilation compilation,
 448        bool isCurrentAssembly,
 449        string generatedNamespace)
 450    {
 3784144451        foreach (var typeSymbol in TypeDiscoveryHelper.GetAllTypes(assembly.GlobalNamespace))
 452        {
 1814465453            if (!TypeDiscoveryHelper.MatchesNamespacePrefix(typeSymbol, namespacePrefixes))
 454                continue;
 455
 1530567456            if (TypeDiscoveryHelper.MatchesExclusionFilter(typeSymbol, excludeNamespacePrefixes))
 457                continue;
 458
 459            // For referenced assemblies, check if the type would be registerable but is inaccessible
 1530567460            if (!isCurrentAssembly && TypeDiscoveryHelper.IsInternalOrLessAccessible(typeSymbol))
 461            {
 462                // Check if this type would have been registered if it were accessible
 73310463                if (TypeDiscoveryHelper.WouldBeInjectableIgnoringAccessibility(typeSymbol) ||
 73310464                    TypeDiscoveryHelper.WouldBePluginIgnoringAccessibility(typeSymbol, compilation.Assembly))
 465                {
 2319466                    var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 2319467                    inaccessibleTypes.Add(new InaccessibleType(typeName, assembly.Name));
 468                }
 2319469                continue; // Skip further processing for inaccessible types
 470            }
 471
 472            // Check for [Options] attribute
 1457257473            if (OptionsAttributeHelper.HasOptionsAttribute(typeSymbol))
 474            {
 167475                var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 167476                var optionsAttrs = OptionsAttributeHelper.GetOptionsAttributes(typeSymbol);
 167477                var sourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 478
 479                // Detect positional record (record with primary constructor parameters)
 167480                var positionalRecordInfo = OptionsDiscoveryHelper.DetectPositionalRecord(typeSymbol);
 481
 482                // Extract bindable properties for AOT code generation
 167483                var properties = OptionsDiscoveryHelper.ExtractBindableProperties(typeSymbol);
 484
 680485                foreach (var optionsAttr in optionsAttrs)
 486                {
 487                    // Determine validator type and method
 173488                    var validatorTypeSymbol = optionsAttr.ValidatorType;
 173489                    var targetType = validatorTypeSymbol ?? typeSymbol; // Look for method on options class or external 
 173490                    var methodName = optionsAttr.ValidateMethod ?? "Validate"; // Convention: "Validate"
 491
 492                    // Find validation method using convention-based discovery
 173493                    var validatorMethodInfo = OptionsAttributeHelper.FindValidationMethod(targetType, methodName);
 173494                    OptionsValidatorInfo? validatorInfo = validatorMethodInfo.HasValue
 173495                        ? new OptionsValidatorInfo(validatorMethodInfo.Value.MethodName, validatorMethodInfo.Value.IsSta
 173496                        : null;
 497
 498                    // Infer section name if not provided
 173499                    var sectionName = optionsAttr.SectionName
 173500                        ?? Helpers.OptionsNamingHelper.InferSectionName(typeSymbol.Name);
 501
 173502                    var validatorTypeName = validatorTypeSymbol != null
 173503                        ? TypeDiscoveryHelper.GetFullyQualifiedName(validatorTypeSymbol)
 173504                        : null;
 505
 173506                    options.Add(new DiscoveredOptions(
 173507                        typeName,
 173508                        sectionName,
 173509                        optionsAttr.Name,
 173510                        optionsAttr.ValidateOnStart,
 173511                        assembly.Name,
 173512                        sourceFilePath,
 173513                        validatorInfo,
 173514                        optionsAttr.ValidateMethod,
 173515                        validatorTypeName,
 173516                        positionalRecordInfo,
 173517                        properties));
 518                }
 519            }
 520
 521            // Check for [HttpClientOptions] attribute
 1457257522            if (HttpClientOptionsAttributeHelper.HasHttpClientOptionsAttribute(typeSymbol))
 523            {
 0524                var httpAttrInfo = HttpClientOptionsAttributeHelper.GetHttpClientOptionsAttribute(typeSymbol);
 0525                if (httpAttrInfo.HasValue)
 526                {
 527                    // Try to read a literal ClientName property body, if any.
 0528                    var clientNamePropResult = HttpClientOptionsAttributeHelper.TryGetClientNameProperty(typeSymbol, out
 0529                    var propertyNameFromType = clientNamePropResult == ClientNamePropertyResult.Literal ? literalValue :
 530
 0531                    if (HttpClientOptionsAttributeHelper.TryResolveClientName(
 0532                        typeSymbol,
 0533                        httpAttrInfo.Value,
 0534                        propertyNameFromType,
 0535                        out var resolvedClientName))
 536                    {
 0537                        var httpSectionName = HttpClientOptionsAttributeHelper.ResolveSectionName(httpAttrInfo.Value, re
 0538                        var httpTypeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 0539                        var httpSourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 0540                        var capabilities = HttpClientOptionsAttributeHelper.DetectCapabilities(typeSymbol);
 541
 0542                        httpClients.Add(new DiscoveredHttpClient(
 0543                            httpTypeName,
 0544                            resolvedClientName,
 0545                            httpSectionName,
 0546                            assembly.Name,
 0547                            capabilities,
 0548                            httpSourceFilePath));
 549                    }
 550                }
 551            }
 552
 553            // Check for [GenerateFactory] attribute - these types get factories instead of direct registration
 1457257554            if (FactoryDiscoveryHelper.HasGenerateFactoryAttribute(typeSymbol))
 555            {
 25556                var factoryConstructors = FactoryDiscoveryHelper.GetFactoryConstructors(typeSymbol);
 25557                if (factoryConstructors.Count > 0)
 558                {
 559                    // Has at least one constructor with runtime params - generate factory
 24560                    var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 24561                    var interfaces = TypeDiscoveryHelper.GetRegisterableInterfaces(typeSymbol, compilation.Assembly);
 30562                    var interfaceNames = interfaces.Select(i => TypeDiscoveryHelper.GetFullyQualifiedName(i)).ToArray();
 24563                    var generationMode = FactoryDiscoveryHelper.GetFactoryGenerationMode(typeSymbol);
 24564                    var returnTypeOverride = FactoryDiscoveryHelper.GetFactoryReturnInterfaceType(typeSymbol);
 24565                    var sourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 566
 24567                    factories.Add(new DiscoveredFactory(
 24568                        typeName,
 24569                        interfaceNames,
 24570                        assembly.Name,
 24571                        generationMode,
 24572                        factoryConstructors.ToArray(),
 24573                        returnTypeOverride,
 24574                        sourceFilePath));
 575
 24576                    continue; // Don't add to injectable types - factory handles registration
 577                }
 578                // If no runtime params, fall through to normal registration (with warning in future analyzer)
 579            }
 580
 581            // Check for DecoratorFor<T> attributes
 1457233582            var decoratorInfos = TypeDiscoveryHelper.GetDecoratorForAttributes(typeSymbol);
 2914504583            foreach (var decoratorInfo in decoratorInfos)
 584            {
 19585                var sourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 19586                decorators.Add(new DiscoveredDecorator(
 19587                    decoratorInfo.DecoratorTypeName,
 19588                    decoratorInfo.ServiceTypeName,
 19589                    decoratorInfo.Order,
 19590                    assembly.Name,
 19591                    sourceFilePath));
 592            }
 593
 594            // Check for OpenDecoratorFor attributes (source-gen only open generic decorators)
 1457233595            var openDecoratorInfos = OpenDecoratorDiscoveryHelper.GetOpenDecoratorForAttributes(typeSymbol);
 2914480596            foreach (var openDecoratorInfo in openDecoratorInfos)
 597            {
 7598                var sourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 7599                openDecorators.Add(new DiscoveredOpenDecorator(
 7600                    openDecoratorInfo.DecoratorType,
 7601                    openDecoratorInfo.OpenGenericInterface,
 7602                    openDecoratorInfo.Order,
 7603                    assembly.Name,
 7604                    sourceFilePath));
 605            }
 606
 607            // Check for Intercept attributes and collect intercepted services
 1457233608            if (InterceptorDiscoveryHelper.HasInterceptAttributes(typeSymbol))
 609            {
 14610                var lifetime = TypeDiscoveryHelper.DetermineLifetime(typeSymbol);
 14611                if (lifetime.HasValue)
 612                {
 14613                    var classLevelInterceptors = InterceptorDiscoveryHelper.GetInterceptAttributes(typeSymbol);
 14614                    var methodLevelInterceptors = InterceptorDiscoveryHelper.GetMethodLevelInterceptAttributes(typeSymbo
 14615                    var methods = InterceptorDiscoveryHelper.GetInterceptedMethods(typeSymbol, classLevelInterceptors, m
 616
 14617                    if (methods.Count > 0)
 618                    {
 14619                        var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 14620                        var interfaces = TypeDiscoveryHelper.GetRegisterableInterfaces(typeSymbol, compilation.Assembly)
 28621                        var interfaceNames = interfaces.Select(i => TypeDiscoveryHelper.GetFullyQualifiedName(i)).ToArra
 622
 623                        // Collect all unique interceptor types
 14624                        var allInterceptorTypes = classLevelInterceptors
 14625                            .Concat(methodLevelInterceptors)
 17626                            .Select(i => i.InterceptorTypeName)
 14627                            .Distinct()
 14628                            .ToArray();
 629
 14630                        var interceptedSourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 631
 14632                        interceptedServices.Add(new DiscoveredInterceptedService(
 14633                            typeName,
 14634                            interfaceNames,
 14635                            assembly.Name,
 14636                            lifetime.Value,
 14637                            methods.ToArray(),
 14638                            allInterceptorTypes,
 14639                            interceptedSourceFilePath));
 640                    }
 641                }
 642            }
 643
 644            // Check for injectable types (but skip types that are providers, which are handled separately)
 1457233645            if (TypeDiscoveryHelper.IsInjectableType(typeSymbol, isCurrentAssembly) && !ProviderDiscoveryHelper.HasProvi
 646            {
 647                // Determine lifetime first - only include types that are actually injectable
 409759648                var lifetime = TypeDiscoveryHelper.DetermineLifetime(typeSymbol);
 409759649                if (lifetime.HasValue)
 650                {
 233538651                    var interfaces = TypeDiscoveryHelper.GetRegisterableInterfaces(typeSymbol, compilation.Assembly);
 233538652                    var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 233791653                    var interfaceNames = interfaces.Select(i => TypeDiscoveryHelper.GetFullyQualifiedName(i)).ToArray();
 654
 655                    // Capture interface locations for navigation
 233538656                    var interfaceInfos = interfaces.Select(i =>
 233538657                    {
 253658                        var ifaceLocation = i.Locations.FirstOrDefault();
 253659                        var ifaceFilePath = ifaceLocation?.SourceTree?.FilePath;
 253660                        var ifaceLine = ifaceLocation?.GetLineSpan().StartLinePosition.Line + 1 ?? 0;
 253661                        return new InterfaceInfo(TypeDiscoveryHelper.GetFullyQualifiedName(i), ifaceFilePath, ifaceLine)
 233538662                    }).ToArray();
 663
 664                    // Check for [DeferToContainer] attribute - use declared types instead of discovered constructors
 233538665                    var deferredParams = TypeDiscoveryHelper.GetDeferToContainerParameterTypes(typeSymbol);
 666                    TypeDiscoveryHelper.ConstructorParameterInfo[] constructorParams;
 233538667                    if (deferredParams != null)
 668                    {
 669                        // DeferToContainer doesn't support keyed services - convert to simple params
 10670                        constructorParams = deferredParams.Select(t => new TypeDiscoveryHelper.ConstructorParameterInfo(
 671                    }
 672                    else
 673                    {
 233533674                        constructorParams = TypeDiscoveryHelper.GetBestConstructorParametersWithKeys(typeSymbol)?.ToArra
 675                    }
 676
 677                    // Get source file path and line for breadcrumbs (null for external assemblies)
 233538678                    var location = typeSymbol.Locations.FirstOrDefault();
 233538679                    var sourceFilePath = location?.SourceTree?.FilePath;
 233538680                    var sourceLine = location?.GetLineSpan().StartLinePosition.Line + 1 ?? 0; // Convert to 1-based
 681
 682                    // Get [Keyed] attribute keys
 233538683                    var serviceKeys = TypeDiscoveryHelper.GetKeyedServiceKeys(typeSymbol);
 684
 685                    // Check if this type implements IDisposable or IAsyncDisposable
 233538686                    var isDisposable = TypeDiscoveryHelper.IsDisposableType(typeSymbol);
 687
 233538688                    injectableTypes.Add(new DiscoveredType(typeName, interfaceNames, assembly.Name, lifetime.Value, cons
 689                }
 690            }
 691
 692            // Check for hosted service types (BackgroundService or IHostedService implementations)
 1457233693            if (TypeDiscoveryHelper.IsHostedServiceType(typeSymbol, isCurrentAssembly))
 694            {
 7695                var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 7696                var constructorParams = TypeDiscoveryHelper.GetBestConstructorParametersWithKeys(typeSymbol)?.ToArray() 
 7697                var sourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 698
 7699                hostedServices.Add(new DiscoveredHostedService(
 7700                    typeName,
 7701                    assembly.Name,
 7702                    GeneratorLifetime.Singleton, // Hosted services are always singleton
 7703                    constructorParams,
 7704                    sourceFilePath));
 705            }
 706
 707            // Check for [Provider] attribute
 1457233708            if (ProviderDiscoveryHelper.HasProviderAttribute(typeSymbol))
 709            {
 18710                var discoveredProvider = ProviderDiscoveryHelper.DiscoverProvider(typeSymbol, assembly.Name, generatedNa
 18711                if (discoveredProvider.HasValue)
 712                {
 18713                    providers.Add(discoveredProvider.Value);
 714                }
 715            }
 716
 717            // Check for plugin types (concrete class with parameterless ctor and interfaces)
 1457233718            if (TypeDiscoveryHelper.IsPluginType(typeSymbol, isCurrentAssembly))
 719            {
 405144720                var pluginInterfaces = TypeDiscoveryHelper.GetPluginInterfaces(typeSymbol, compilation.Assembly);
 405144721                if (pluginInterfaces.Count > 0)
 722                {
 1406723                    var typeName = TypeDiscoveryHelper.GetFullyQualifiedName(typeSymbol);
 2817724                    var interfaceNames = pluginInterfaces.Select(i => TypeDiscoveryHelper.GetFullyQualifiedName(i)).ToAr
 1406725                    var attributeNames = TypeDiscoveryHelper.GetPluginAttributes(typeSymbol).ToArray();
 1406726                    var sourceFilePath = typeSymbol.Locations.FirstOrDefault()?.SourceTree?.FilePath;
 1406727                    var order = PluginOrderHelper.GetPluginOrder(typeSymbol);
 728
 1406729                    pluginTypes.Add(new DiscoveredPlugin(typeName, interfaceNames, assembly.Name, attributeNames, source
 730                }
 731            }
 732
 733            // Check for IHubRegistrationPlugin implementations
 734            // NOTE: SignalR hub discovery is now handled by NexusLabs.Needlr.SignalR.Generators
 735
 736            // Check for SemanticKernel plugin types (classes/statics with [KernelFunction] methods)
 737            // NOTE: SemanticKernel plugin discovery is now handled by NexusLabs.Needlr.SemanticKernel.Generators
 738        }
 77607739    }
 740
 741    private static string GenerateTypeRegistrySource(DiscoveryResult discoveryResult, string assemblyName, BreadcrumbWri
 742    {
 455743        var builder = new StringBuilder();
 455744        var safeAssemblyName = GeneratorHelpers.SanitizeIdentifier(assemblyName);
 455745        var hasOptions = discoveryResult.Options.Count > 0;
 455746        var hasHttpClients = discoveryResult.HttpClients.Count > 0;
 455747        var hasConfigBoundRegistrations = hasOptions || hasHttpClients;
 748
 455749        breadcrumbs.WriteFileHeader(builder, assemblyName, "Needlr Type Registry");
 455750        builder.AppendLine("#nullable enable");
 455751        builder.AppendLine();
 455752        builder.AppendLine("using System;");
 455753        builder.AppendLine("using System.Collections.Generic;");
 455754        builder.AppendLine();
 455755        if (hasConfigBoundRegistrations)
 756        {
 146757            builder.AppendLine("using Microsoft.Extensions.Configuration;");
 146758            if (isAotProject || hasHttpClients)
 759            {
 78760                builder.AppendLine("using Microsoft.Extensions.Options;");
 761            }
 762        }
 455763        builder.AppendLine("using Microsoft.Extensions.DependencyInjection;");
 455764        builder.AppendLine();
 455765        builder.AppendLine("using NexusLabs.Needlr;");
 455766        builder.AppendLine("using NexusLabs.Needlr.Generators;");
 455767        builder.AppendLine();
 455768        builder.AppendLine($"namespace {safeAssemblyName}.Generated;");
 455769        builder.AppendLine();
 455770        builder.AppendLine("/// <summary>");
 455771        builder.AppendLine("/// Compile-time generated registry of injectable types and plugins.");
 455772        builder.AppendLine("/// This eliminates the need for runtime reflection-based type discovery.");
 455773        builder.AppendLine("/// </summary>");
 455774        builder.AppendLine("[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"NexusLabs.Needlr.Generators\", \"1
 455775        builder.AppendLine("public static class TypeRegistry");
 455776        builder.AppendLine("{");
 777
 455778        CodeGen.InjectableTypesCodeGenerator.GenerateInjectableTypesArray(builder, discoveryResult.InjectableTypes, brea
 455779        builder.AppendLine();
 455780        CodeGen.PluginsCodeGenerator.GeneratePluginTypesArray(builder, discoveryResult.PluginTypes, breadcrumbs, project
 781
 455782        builder.AppendLine();
 455783        builder.AppendLine("    /// <summary>");
 455784        builder.AppendLine("    /// Gets all injectable types discovered at compile time.");
 455785        builder.AppendLine("    /// </summary>");
 455786        builder.AppendLine("    /// <returns>A read-only list of injectable type information.</returns>");
 455787        builder.AppendLine("    public static IReadOnlyList<InjectableTypeInfo> GetInjectableTypes() => _types;");
 455788        builder.AppendLine();
 455789        builder.AppendLine("    /// <summary>");
 455790        builder.AppendLine("    /// Gets all plugin types discovered at compile time.");
 455791        builder.AppendLine("    /// </summary>");
 455792        builder.AppendLine("    /// <returns>A read-only list of plugin type information.</returns>");
 455793        builder.AppendLine("    public static IReadOnlyList<PluginTypeInfo> GetPluginTypes() => _plugins;");
 794
 455795        if (hasConfigBoundRegistrations)
 796        {
 146797            builder.AppendLine();
 146798            GenerateRegisterOptionsMethod(builder, discoveryResult.Options, discoveryResult.HttpClients, safeAssemblyNam
 799        }
 800
 455801        if (discoveryResult.Providers.Count > 0)
 802        {
 17803            builder.AppendLine();
 17804            CodeGen.DecoratorsCodeGenerator.GenerateRegisterProvidersMethod(builder, discoveryResult.Providers, safeAsse
 805        }
 806
 455807        builder.AppendLine();
 455808        CodeGen.DecoratorsCodeGenerator.GenerateApplyDecoratorsMethod(builder, discoveryResult.Decorators, discoveryResu
 809
 455810        if (discoveryResult.HostedServices.Count > 0)
 811        {
 6812            builder.AppendLine();
 6813            CodeGen.DecoratorsCodeGenerator.GenerateRegisterHostedServicesMethod(builder, discoveryResult.HostedServices
 814        }
 815
 455816        builder.AppendLine("}");
 817
 455818        return builder.ToString();
 819    }
 820
 821    private static void GenerateRegisterOptionsMethod(StringBuilder builder, IReadOnlyList<DiscoveredOptions> options, I
 822    {
 146823        builder.AppendLine("    /// <summary>");
 146824        builder.AppendLine("    /// Registers all discovered options types with the service collection.");
 146825        builder.AppendLine("    /// This binds configuration sections to strongly-typed options classes,");
 146826        builder.AppendLine("    /// and wires up named HttpClient registrations for [HttpClientOptions] types.");
 146827        builder.AppendLine("    /// </summary>");
 146828        builder.AppendLine("    /// <param name=\"services\">The service collection to configure.</param>");
 146829        builder.AppendLine("    /// <param name=\"configuration\">The configuration root to bind options from.</param>")
 146830        builder.AppendLine("    public static void RegisterOptions(IServiceCollection services, IConfiguration configura
 146831        builder.AppendLine("    {");
 832
 146833        if (options.Count == 0 && httpClients.Count == 0)
 834        {
 0835            breadcrumbs.WriteInlineComment(builder, "        ", "No options or HttpClient types discovered");
 836        }
 837        else
 838        {
 146839            if (options.Count > 0)
 840            {
 146841                if (isAotProject)
 842                {
 78843                    CodeGen.OptionsCodeGenerator.GenerateAotOptionsRegistration(builder, options, safeAssemblyName, brea
 844                }
 845                else
 846                {
 68847                    CodeGen.OptionsCodeGenerator.GenerateReflectionOptionsRegistration(builder, options, safeAssemblyNam
 848                }
 849            }
 850
 146851            if (httpClients.Count > 0)
 852            {
 0853                CodeGen.HttpClientCodeGenerator.EmitHttpClientRegistrations(builder, httpClients);
 854            }
 855        }
 856
 146857        builder.AppendLine("    }");
 146858    }
 859
 860}

Methods/Properties

Initialize(Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext)
GetBreadcrumbLevel(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider)
GetProjectDirectory(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider)
GetDiagnosticOptions(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider)
ShouldExportGraph(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider)
IsAotProject(Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider)
GetAttributeInfoFromCompilation(Microsoft.CodeAnalysis.Compilation)
DiscoverTypes(Microsoft.CodeAnalysis.Compilation,System.String[],System.String[],System.Boolean)
CollectTypesFromAssembly(Microsoft.CodeAnalysis.IAssemblySymbol,System.Collections.Generic.IReadOnlyList`1<System.String>,System.Collections.Generic.IReadOnlyList`1<System.String>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredType>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredPlugin>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredDecorator>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredOpenDecorator>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredInterceptedService>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredFactory>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredOptions>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredHostedService>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredProvider>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.DiscoveredHttpClient>,System.Collections.Generic.List`1<NexusLabs.Needlr.Generators.Models.InaccessibleType>,Microsoft.CodeAnalysis.Compilation,System.Boolean,System.String)
GenerateTypeRegistrySource(NexusLabs.Needlr.Generators.Models.DiscoveryResult,System.String,NexusLabs.Needlr.Generators.BreadcrumbWriter,System.String,System.Boolean)
GenerateRegisterOptionsMethod(System.Text.StringBuilder,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Models.DiscoveredOptions>,System.Collections.Generic.IReadOnlyList`1<NexusLabs.Needlr.Generators.Models.DiscoveredHttpClient>,System.String,NexusLabs.Needlr.Generators.BreadcrumbWriter,System.String,System.Boolean)