From c56d3b2874da2d843b847a4f822dd2bd173c2031 Mon Sep 17 00:00:00 2001 From: Przemek Rogala Date: Mon, 31 Aug 2026 14:42:23 +0200 Subject: [PATCH 1/2] fix: eliminate CS8625/CS8604 nullable warnings in generated Build/BuildDefault Non-nullable reference properties without a fixture, mock, or default value fell back to `default(T)` in generated code, which the compiler flags as a null-to-non-nullable conversion. The generator suppressed the resulting CS8604 with a pragma but never suppressed the underlying CS8625, so any consumer with WarningsAsErrors=nullable (as this repo itself uses) would hit a hard build error instead of a warning. Apply the null-forgiving operator directly to the default(T) fallback, matching the pattern already used for the AutoFixture branch, instead of pragma-suppressing the whole method body. Enable Nullable on the "without assembly info" integration test project to cover the no-fixture, no-mock, nullable-enabled path, and add a BuildDefault() runtime test that was previously only compiled, never executed. Co-Authored-By: Claude Opus 4.8 --- Buildenator/CodeAnalysis/TypedSymbol.cs | 2 +- Buildenator/Configuration/EntityToBuild.cs | 26 +++++-------------- ...tionTests.SourceWithoutAssemblyInfo.csproj | 1 + ...ildersGeneratorMandatoryParametersTests.cs | 10 +++++++ 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Buildenator/CodeAnalysis/TypedSymbol.cs b/Buildenator/CodeAnalysis/TypedSymbol.cs index 900bc7b..e1f179a 100644 --- a/Buildenator/CodeAnalysis/TypedSymbol.cs +++ b/Buildenator/CodeAnalysis/TypedSymbol.cs @@ -174,7 +174,7 @@ public string GenerateLazyFieldValueReturn() + (_nullableStrategy == NullableStrategy.Enabled ? "!" : "") : GetDefaultValueName() is { } defaultValueName ? defaultValueName - : $"default({TypeFullName})")})).Object"; + : $"default({TypeFullName})" + (_nullableStrategy == NullableStrategy.Enabled ? "!" : ""))})).Object"; public string GenerateFieldValueReturn() => IsMockable() diff --git a/Buildenator/Configuration/EntityToBuild.cs b/Buildenator/Configuration/EntityToBuild.cs index 8dffdb5..52aaa03 100644 --- a/Buildenator/Configuration/EntityToBuild.cs +++ b/Buildenator/Configuration/EntityToBuild.cs @@ -68,19 +68,12 @@ public string GenerateBuildsCode(bool shouldGenerateMethodsForUnreachablePropert if (ConstructorToBuild is null) return ""; - var disableWarning = NullableStrategy == NullableStrategy.Enabled - ? "#pragma warning disable CS8604\n" - : string.Empty; - var restoreWarning = NullableStrategy == NullableStrategy.Enabled - ? "#pragma warning restore CS8604\n" - : string.Empty; - - return $@"{disableWarning} public {FullName} {DefaultConstants.BuildMethodName}() + return $@" public {FullName} {DefaultConstants.BuildMethodName}() {{ {DefaultConstants.PreBuildMethodName}(); {GenerateLazyBuildEntityString(shouldGenerateMethodsForUnreachableProperties, ConstructorToBuild.Parameters)} }} -{restoreWarning} + "; } @@ -151,26 +144,21 @@ public string GenerateDefaultBuildsCode() .Aggregate(new StringBuilder(), (builder, s) => builder.AppendLine(s)) .ToString(); + var nullForgiving = NullableStrategy == NullableStrategy.Enabled ? "!" : string.Empty; var methodParameters = ConstructorToBuild.Parameters .Concat(_properties) .Select(s => { var fieldType = s.GenerateFieldType(); - return $"{fieldType} {s.UnderScoreName} = default({fieldType})"; + return $"{fieldType} {s.UnderScoreName} = default({fieldType}){nullForgiving}"; }).ComaJoin(); - var disableWarning = NullableStrategy == NullableStrategy.Enabled - ? "#pragma warning disable CS8625\n" - : string.Empty; - var restoreWarning = NullableStrategy == NullableStrategy.Enabled - ? "#pragma warning restore CS8625\n" - : string.Empty; - - return $@"{disableWarning} public static {FullName} BuildDefault({methodParameters}) + + return $@" public static {FullName} BuildDefault({methodParameters}) {{ {moqInit} {GenerateDefaultBuildEntityString(ConstructorToBuild.Parameters)} }} -{restoreWarning}"; +"; } private string GenerateDefaultBuildEntityString(IEnumerable parameters) diff --git a/Tests/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo.csproj b/Tests/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo.csproj index c4f96f8..433ec24 100644 --- a/Tests/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo.csproj +++ b/Tests/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo/Buildenator.IntegrationTests.SourceWithoutAssemblyInfo.csproj @@ -3,6 +3,7 @@ net10.0 false + enable diff --git a/Tests/Buildenator.IntegrationTests/BuildersGeneratorMandatoryParametersTests.cs b/Tests/Buildenator.IntegrationTests/BuildersGeneratorMandatoryParametersTests.cs index 56a768f..dc13915 100644 --- a/Tests/Buildenator.IntegrationTests/BuildersGeneratorMandatoryParametersTests.cs +++ b/Tests/Buildenator.IntegrationTests/BuildersGeneratorMandatoryParametersTests.cs @@ -51,4 +51,14 @@ public void BuildersGenerator_MandatoryParameters_NoStaticFactoryPropertyGenerat staticProperty.Should().BeNull("static factory property is incompatible with mandatory constructor parameters"); } + + [Fact] + public void BuildersGenerator_MandatoryParameters_BuildDefaultUsesDefaultValues() + { + var result = EntityWithMandatoryConstructorParametersBuilder.BuildDefault(); + + result.LineNumber.Should().Be(0); + result.Activity.Should().BeNull(); + result.Tags.Should().BeNull(); + } } From 57f8e87295bb98e97c76a7087b68e6723bf41724 Mon Sep 17 00:00:00 2001 From: Przemek Rogala Date: Mon, 31 Aug 2026 14:44:15 +0200 Subject: [PATCH 2/2] chore: bump version to 10.8.2.0 and document the nullable warnings fix --- Buildenator/Buildenator.csproj | 2 +- CHANGELOG.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Buildenator/Buildenator.csproj b/Buildenator/Buildenator.csproj index 7152b0a..5cc7b8c 100644 --- a/Buildenator/Buildenator.csproj +++ b/Buildenator/Buildenator.csproj @@ -9,7 +9,7 @@ false true - 10.8.1.0 + 10.8.2.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index b82a19c..abf45a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 10.8.2.0 - 2026-8-31 + +### Fixed +- **CS8625/CS8604 nullable warnings in generated `Build()`/`BuildDefault()`**: Non-nullable reference properties without a fixture, mock, or `Default{PropertyName}` value fell back to `default(T)` in the generated code, which the compiler flags as a null-to-non-nullable-type conversion. `Build()` suppressed the resulting CS8604 with a pragma but never suppressed the underlying CS8625, so consumers with `WarningsAsErrors=nullable` (or similarly strict nullable settings) could hit a hard build error instead of a warning. The `default(T)` fallback now uses the null-forgiving operator (`!`) directly, the same pattern already used for the AutoFixture branch, instead of pragma-suppressing the whole method body. Both `Build()` and `BuildDefault()` are now generated warning-free with no pragmas needed. + ## 10.8.1.0 - 2026-4-12 ### Fixed