diff --git a/CHANGELOG.md b/CHANGELOG.md index c30ddcb..41143c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **DP086 assemble-contract copy**: message/description now name sync product `T` and async `Task` / `ValueTask` as valid `[BuilderAssemble]` returns, and call out void, bare `Task`/`ValueTask`, duplicate assemble, and multiple `CancellationToken` as illegal ([#325](https://github.com/Skymly/DesignPatterns/issues/325); Relates to [#324](https://github.com/Skymly/DesignPatterns/issues/324)). - **F3 Work Graph MVP complete**: ROADMAP Top-3 marked `[x]` after Samples [DesignPatterns.Samples#25](https://github.com/Skymly/DesignPatterns.Samples/issues/25) / [PR#28](https://github.com/Skymly/DesignPatterns.Samples/pull/28); Design Doc references the request-prep sample; Spec [#308](https://github.com/Skymly/DesignPatterns/issues/308) closed (Phase 2+ Dop/trace/DI/unregistered Analyzer deferred). - **Peer-presence Unregistered\* base**: extract `UnregisteredPayloadPeerAnalyzerBase` for DP024 / DP044 / DP072; `UnregisteredHandlerAnalyzer`, `UnregisteredEventHandlerAnalyzer`, and `UnregisteredCommandHandlerAnalyzer` become thin adapters supplying attribute-peer and declared-peer extraction. Strategy/Factory contract-peer base unchanged. Diagnostic behaviour frozen (including no `IStreamCommandHandler` peer-presence). - **DI registration map consumer seam**: `CaptiveDependencyAnalyzer` and `SingletonLifecycleAnalyzer` obtain the map only via `DiRegistrationMap.Build(Compilation)` at compilation analysis time; incremental `DiRegistrationMapBuilder` wiring is a private implementation detail of `Build`. Diagnostic behaviour for DP062/066/068–071 is unchanged. `Build` skips generated syntax trees (`.g.cs` / auto-generated headers) so visibility matches the prior `RegisterSyntaxNodeAction` + `GeneratedCodeAnalysisFlags.None` path. diff --git a/DesignPatterns.Diagnostics/DesignPatternsDiagnosticDescriptors.cs b/DesignPatterns.Diagnostics/DesignPatternsDiagnosticDescriptors.cs index 0c87942..f2ef286 100644 --- a/DesignPatterns.Diagnostics/DesignPatternsDiagnosticDescriptors.cs +++ b/DesignPatterns.Diagnostics/DesignPatternsDiagnosticDescriptors.cs @@ -736,8 +736,8 @@ public static class DesignPatternsDiagnosticDescriptors public static DiagnosticDescriptor GenerateBuilderAssembleContractMismatch { get; } = Create( DiagnosticIds.GenerateBuilderAssembleContractMismatch, "GenerateBuilder assemble method contract is invalid", - "Method '{0}' on holder '{1}' is marked [BuilderAssemble] but does not satisfy the assemble contract (exactly one assemble method returning a non-void product type). Fix the signature or remove duplicate [BuilderAssemble] attributes.", - "The assemble method is the sole product factory for the generated builder and must return the product type.", + "Method '{0}' on holder '{1}' is marked [BuilderAssemble] but does not satisfy the assemble contract (exactly one assemble method returning sync product T, or async Task / ValueTask; not void, bare Task/ValueTask, or multiple CancellationToken parameters). Fix the signature or remove duplicate [BuilderAssemble] attributes.", + "The assemble method is the sole product factory for the generated builder. Return sync product type T, or async Task/ValueTask. Reject void, bare Task/ValueTask, duplicate [BuilderAssemble], and more than one CancellationToken.", DiagnosticSeverity.Error, GeneratorCategory); diff --git a/tests/DesignPatterns.SourceGenerators.Tests/Generators/GenerateBuilderGeneratorTests.ReportsDp086WhenAssembleReturnsVoid.verified.txt b/tests/DesignPatterns.SourceGenerators.Tests/Generators/GenerateBuilderGeneratorTests.ReportsDp086WhenAssembleReturnsVoid.verified.txt index ad29f01..9c07385 100644 --- a/tests/DesignPatterns.SourceGenerators.Tests/Generators/GenerateBuilderGeneratorTests.ReportsDp086WhenAssembleReturnsVoid.verified.txt +++ b/tests/DesignPatterns.SourceGenerators.Tests/Generators/GenerateBuilderGeneratorTests.ReportsDp086WhenAssembleReturnsVoid.verified.txt @@ -1,6 +1,6 @@ [ { Id: DP086, - Message: Method 'Assemble' on holder 'VoidAssembleSchema' is marked [BuilderAssemble] but does not satisfy the assemble contract (exactly one assemble method returning a non-void product type). Fix the signature or remove duplicate [BuilderAssemble] attributes. + Message: Method 'Assemble' on holder 'VoidAssembleSchema' is marked [BuilderAssemble] but does not satisfy the assemble contract (exactly one assemble method returning sync product T, or async Task / ValueTask; not void, bare Task/ValueTask, or multiple CancellationToken parameters). Fix the signature or remove duplicate [BuilderAssemble] attributes. } ] \ No newline at end of file diff --git a/tests/DesignPatterns.SourceGenerators.Tests/StepBuilderDiagnosticDescriptorsTests.cs b/tests/DesignPatterns.SourceGenerators.Tests/StepBuilderDiagnosticDescriptorsTests.cs index 87f699f..41a2ab2 100644 --- a/tests/DesignPatterns.SourceGenerators.Tests/StepBuilderDiagnosticDescriptorsTests.cs +++ b/tests/DesignPatterns.SourceGenerators.Tests/StepBuilderDiagnosticDescriptorsTests.cs @@ -145,13 +145,20 @@ public void Invalid_holder_descriptor_is_generator_error_with_actionable_message public void Assemble_contract_mismatch_descriptor_is_generator_error_with_actionable_message() { var descriptor = DesignPatternsDiagnosticDescriptors.GenerateBuilderAssembleContractMismatch; + var message = descriptor.MessageFormat.ToString(); + var description = descriptor.Description.ToString(); Assert.Equal(DiagnosticIds.GenerateBuilderAssembleContractMismatch, descriptor.Id); Assert.Equal(DiagnosticSeverity.Error, descriptor.DefaultSeverity); Assert.Equal("DesignPatterns.Generators", descriptor.Category); Assert.Equal(DiagnosticHelpLinks.For(descriptor.Id), descriptor.HelpLinkUri); - Assert.False(string.IsNullOrWhiteSpace(descriptor.Description.ToString())); - Assert.Contains("[BuilderAssemble]", descriptor.MessageFormat.ToString(), StringComparison.Ordinal); - Assert.Contains("{0}", descriptor.MessageFormat.ToString(), StringComparison.Ordinal); + Assert.False(string.IsNullOrWhiteSpace(description)); + Assert.Contains("[BuilderAssemble]", message, StringComparison.Ordinal); + Assert.Contains("{0}", message, StringComparison.Ordinal); + Assert.Contains("{1}", message, StringComparison.Ordinal); + Assert.Contains("Task", message, StringComparison.Ordinal); + Assert.Contains("ValueTask", message, StringComparison.Ordinal); + Assert.Contains("Task", description, StringComparison.Ordinal); + Assert.Contains("ValueTask", description, StringComparison.Ordinal); } }