< Summary

Information
Class: NexusLabs.Needlr.Generators.CodeGen.GeneratedConstructorCodeGenerator
Assembly: NexusLabs.Needlr.Generators
File(s): /_work/nick-alienware-needlr-1-1784953859-3e8997/needlr/needlr/src/NexusLabs.Needlr.Generators/CodeGen/GeneratedConstructorCodeGenerator.cs
Line coverage
100%
Covered lines: 53
Uncovered lines: 0
Coverable lines: 53
Total lines: 119
Line coverage: 100%
Branch coverage
94%
Covered branches: 17
Total branches: 18
Branch coverage: 94.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
BuildHintName(...)50%22100%
GenerateConstructorSource(...)100%1414100%
ToCrefTypeParameterList(...)100%22100%

File(s)

/_work/nick-alienware-needlr-1-1784953859-3e8997/needlr/needlr/src/NexusLabs.Needlr.Generators/CodeGen/GeneratedConstructorCodeGenerator.cs

#LineLine coverage
 1using System;
 2using System.Linq;
 3using System.Text;
 4
 5using NexusLabs.Needlr.Generators.Models;
 6
 7namespace NexusLabs.Needlr.Generators.CodeGen;
 8
 9/// <summary>
 10/// Emits the generated public constructor for a partial class from a
 11/// <see cref="GeneratedConstructorModel"/>.
 12/// </summary>
 13internal static class GeneratedConstructorCodeGenerator
 14{
 15    private const string GeneratedConstructorFileSuffix = ".GeneratedConstructor.g.cs";
 16
 17    /// <summary>
 18    /// Builds the stable <c>AddSource</c> hint name for a generated-constructor
 19    /// model, deterministically derived from the type's namespace, name, and generic
 20    /// arity -- with no mutable, encounter-order-dependent counter involved. Including
 21    /// arity is required because a namespace can legally declare both <c>Foo</c> and
 22    /// <c>Foo&lt;T&gt;</c> as distinct types; the arity suffix (mirroring how the CLR
 23    /// itself disambiguates same-named generic arities via a metadata name like
 24    /// <c>Foo`1</c>) keeps their hint names from colliding.
 25    /// </summary>
 26    internal static string BuildHintName(GeneratedConstructorModel model)
 27    {
 6628        var baseName = model.ContainingNamespace.Length > 0
 6629            ? $"{model.ContainingNamespace}.{model.ContainingTypeName}"
 6630            : model.ContainingTypeName;
 31
 6632        var safeName = GeneratorHelpers.SanitizeIdentifier(baseName);
 33
 6634        return $"{safeName}_T{model.Arity}{GeneratedConstructorFileSuffix}";
 35    }
 36
 37    internal static string GenerateConstructorSource(GeneratedConstructorModel model, string assemblyName, BreadcrumbWri
 38    {
 6639        var builder = new StringBuilder();
 40
 6641        breadcrumbs.WriteFileHeader(builder, assemblyName, $"Generated constructor for {model.ContainingTypeName}{model.
 6642        builder.AppendLine("#nullable enable");
 6643        builder.AppendLine();
 44
 6645        if (model.ContainingNamespace.Length > 0)
 46        {
 6647            builder.AppendLine($"namespace {model.ContainingNamespace};");
 6648            builder.AppendLine();
 49        }
 50
 6651        breadcrumbs.WriteInlineComment(builder, string.Empty, $"Constructor generated from {model.Fields.Length} eligibl
 6652        builder.AppendLine($"partial class {model.ContainingTypeName}{model.TypeParameterList}");
 6653        builder.AppendLine("{");
 54
 6655        var effectiveGuards = model.Fields
 7556            .Select(field => ConstructorGuardCodeGenerator.ComposeEffectiveGuards(
 7557                model.NullGuardMode == GeneratedConstructorNullGuardMode.NonNullableReferences &&
 7558                    field.IsNonNullableReferenceType,
 7559                field.ExplicitGuards))
 6660            .ToArray();
 61
 6662        builder.AppendLine("    /// <summary>");
 6663        builder.AppendLine($"    /// Initializes a new instance of the <see cref=\"{model.ContainingTypeName}{ToCrefType
 6664        builder.AppendLine("    /// </summary>");
 65
 28266        foreach (var field in model.Fields)
 67        {
 7568            builder.AppendLine($"    /// <param name=\"{field.ParameterName}\">The value used to initialize <c>{field.Fi
 69        }
 70
 6671        ConstructorGuardCodeGenerator.WriteExceptionDocumentation(
 6672            builder,
 7573            model.Fields.Select(field => field.ParameterName).ToArray(),
 6674            effectiveGuards,
 6675            "    ");
 76
 14177        var parameterList = string.Join(", ", model.Fields.Select(f => $"{f.ParameterTypeName} {f.ParameterName}"));
 6678        builder.AppendLine($"    public {model.ContainingTypeName}({parameterList})");
 6679        builder.AppendLine("    {");
 80
 6681        var emittedAnyGuard = false;
 28282        for (var i = 0; i < model.Fields.Length; i++)
 83        {
 7584            var field = model.Fields[i];
 23685            foreach (var guard in effectiveGuards[i])
 86            {
 4387                var guardCall = ConstructorGuardCodeGenerator.BuildGuardCall(
 4388                    guard,
 4389                    field.ParameterName);
 4390                builder.AppendLine($"        {guardCall}");
 4391                emittedAnyGuard = true;
 92            }
 93        }
 94
 6695        if (emittedAnyGuard)
 96        {
 3797            builder.AppendLine();
 98        }
 99
 282100        foreach (var field in model.Fields)
 101        {
 75102            builder.AppendLine($"        {field.FieldName} = {field.ParameterName};");
 103        }
 104
 66105        builder.AppendLine("    }");
 66106        builder.AppendLine("}");
 107
 66108        return builder.ToString();
 109    }
 110
 111    private static string ToCrefTypeParameterList(string typeParameterList)
 112    {
 66113        if (typeParameterList.Length == 0)
 62114            return string.Empty;
 115
 4116        return typeParameterList.Replace('<', '{').Replace('>', '}');
 117    }
 118
 119}