Allow Behaviors with 0, 1, or 2 generic type parameters#289
Allow Behaviors with 0, 1, or 2 generic type parameters#289
Conversation
Co-authored-by: viceroypenguin <1992555+viceroypenguin@users.noreply.github.com>
Co-authored-by: viceroypenguin <1992555+viceroypenguin@users.noreply.github.com>
…behaviors Co-authored-by: viceroypenguin <1992555+viceroypenguin@users.noreply.github.com>
viceroypenguin
left a comment
There was a problem hiding this comment.
- Test changed in
InvalidBehaviorsTestsshould be changed to demonstrate that the generator does not generate any code when a behavior with 3 generic parameters is specified. - Add tests in
BehaviorTestsfor both zero and one type-parameter behaviors - Add new functional tests under
Immediate.Handlers.FunctionalTests;BehaviorTests.csto prove code builds and runs with zero and one type-parameter behaviors
- Changed InvalidBehaviorsTests to demonstrate that behaviors with 3 generic parameters produce no code - Added ZeroTypeParameterBehavior and OneTypeParameterBehavior tests to BehaviorTests - Added comprehensive functional tests in PartiallySpecifiedBehaviorTests to prove zero and one type-parameter behaviors compile and run correctly Co-authored-by: viceroypenguin <1992555+viceroypenguin@users.noreply.github.com>
Completed in commit c0eab83:
All 131 tests pass. |
…Nothing test Co-authored-by: viceroypenguin <1992555+viceroypenguin@users.noreply.github.com>
viceroypenguin
left a comment
There was a problem hiding this comment.
Check new tests and update analyzer and generator codes to make tests pass
…iors - Added IHR0020 diagnostic to detect when behavior type arguments don't match handler types - Updated analyzer to validate behavior type compatibility with handlers - Updated generator to detect which position (request/response) is parameterized in single-param behaviors - Still needs fixes to template generation logic to properly construct single-param behaviors Co-authored-by: viceroypenguin <1992555+viceroypenguin@users.noreply.github.com>
Started working on this in commit 8f2f6e6. I've:
Still working on fixing the template generation logic - the tests expect behaviors where a single type parameter can map to either the request OR response position, which requires more sophisticated template handling than the current implementation. The generator needs to construct behaviors like Current status: 9 tests failing, all related to single-parameter behavior code generation. |
There was a problem hiding this comment.
Pull request overview
Expands the Immediate.Handlers behavior model so behaviors can be declared with 0, 1, or 2 generic type parameters (instead of exactly 2), updates the generator/analyzers accordingly, and adds tests/functional coverage for partially specified behaviors.
Changes:
- Analyzer: allow 0–2 generic parameters for behaviors; add IHR0020 (behavior request/response compatibility) and IHR0021 (invalid behavior constraints).
- Generator: compute behavior constraint info and generate correct behavior type arguments for 0/1/2-parameter behaviors; filter out incompatible behaviors.
- Tests: add analyzer, generator snapshot, and functional tests for non-generic and single-generic behaviors.
Reviewed changes
Copilot reviewed 52 out of 52 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Immediate.Handlers.Tests/GeneratorTests/InvalidBehaviorsTests.cs | Updates generator invalid-behavior test inputs (e.g., static classes become partial); removes cases that are now valid. |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.cs | Adds generator tests for 0/1 type-parameter behaviors and constraint-filtering scenarios. |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.ZeroTypeParameterBehavior_assemblies=Normal#IH.Dummy.GetUsersQuery.g.verified.cs | New verified output snapshot for non-generic behavior pipeline generation (Normal). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.ZeroTypeParameterBehavior_assemblies=Msdi#IH.ServiceCollectionExtensions.g.verified.cs | New verified output snapshot for DI registrations for non-generic behaviors (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.ZeroTypeParameterBehavior_assemblies=Msdi#IH.Dummy.GetUsersQuery.g.verified.cs | New verified output snapshot for non-generic behavior pipeline generation (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.ResponseTypeParameterBehavior_assemblies=Normal#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot for single-parameter behavior (response varies) generation (Normal). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.ResponseTypeParameterBehavior_assemblies=Msdi#IH.ServiceCollectionExtensions.g.verified.cs | New verified snapshot for DI registrations for single-parameter behavior (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.ResponseTypeParameterBehavior_assemblies=Msdi#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot for handler pipeline with single-parameter behavior (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.RequestTypeParameterBehavior_assemblies=Normal#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot for single-parameter behavior (request varies) generation (Normal). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.RequestTypeParameterBehavior_assemblies=Msdi#IH.ServiceCollectionExtensions.g.verified.cs | New verified snapshot for DI registrations for single-parameter behavior (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.RequestTypeParameterBehavior_assemblies=Msdi#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot for handler pipeline with single-parameter behavior (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.BehaviorHasMultipleTResponseConstraints_assemblies=Normal#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot ensuring invalid/multi constraints cause behavior to be dropped from pipeline (Normal). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.BehaviorHasMultipleTResponseConstraints_assemblies=Msdi#IH.ServiceCollectionExtensions.g.verified.cs | New verified snapshot for DI registrations in multi-constraint case (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.BehaviorHasMultipleTResponseConstraints_assemblies=Msdi#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot for handler pipeline in multi-constraint case (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.BehaviorHasMultipleTRequestConstraints_assemblies=Normal#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot ensuring invalid/multi constraints cause behavior to be dropped from pipeline (Normal). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.BehaviorHasMultipleTRequestConstraints_assemblies=Msdi#IH.ServiceCollectionExtensions.g.verified.cs | New verified snapshot for DI registrations in multi-constraint case (Msdi). |
| tests/Immediate.Handlers.Tests/GeneratorTests/BehaviorTests.BehaviorHasMultipleTRequestConstraints_assemblies=Msdi#IH.Dummy.GetUsersQuery.g.verified.cs | New verified snapshot for handler pipeline in multi-constraint case (Msdi). |
| tests/Immediate.Handlers.Tests/AnalyzerTests/HandlerClassAnalyzerTests/Tests.HandlerUsesBehaviorTypeWithZeroGenericParameters.cs | Adds handler-level analyzer tests for non-generic behaviors. |
| tests/Immediate.Handlers.Tests/AnalyzerTests/HandlerClassAnalyzerTests/Tests.HandlerUsesBehaviorTypeWithOneGenericParameter.cs | Adds handler-level analyzer tests for single-generic behaviors and IHR0020 mismatch cases. |
| tests/Immediate.Handlers.Tests/AnalyzerTests/HandlerClassAnalyzerTests/Tests.HandlerUsesBehaviorTypeIsMultiplyInherited.cs | Adds handler-level analyzer tests for behaviors inheriting Behavior<,> indirectly (and mismatch detection). |
| tests/Immediate.Handlers.Tests/AnalyzerTests/BehaviorClassAnalyzerTests.cs | Adds tests for new behavior constraint validation analyzer (IHR0021). |
| tests/Immediate.Handlers.Tests/AnalyzerTests/BehaviorAnalyzerTests/Tests.BehaviorTypeWithZeroGenericParameters.cs | Adds BehaviorsAnalyzer tests validating 0-generic behavior types are accepted. |
| tests/Immediate.Handlers.Tests/AnalyzerTests/BehaviorAnalyzerTests/Tests.BehaviorTypeWithOneGenericParameter.cs | Adds BehaviorsAnalyzer tests validating 1-generic behavior types are accepted. |
| tests/Immediate.Handlers.Tests/AnalyzerTests/BehaviorAnalyzerTests/Tests.BehaviorTypeHasThreeGenericParameters.cs | Renames/adjusts test to match new “3+ generic params invalid” rule. |
| tests/Immediate.Handlers.FunctionalTests/NoBehaviors/ParameterlessTests.cs | Updates functional test handler signatures to include CancellationToken. |
| tests/Immediate.Handlers.FunctionalTests/Immediate.Handlers.FunctionalTests.csproj | Adds analyzer reference + diagnostics testing package for functional tests. |
| tests/Immediate.Handlers.FunctionalTests/Behavior/PartiallySpecifiedBehaviorTests.cs | Adds functional test exercising 0/1/2-parameter behaviors in a pipeline. |
| tests/Immediate.Handlers.FunctionalTests/Behavior/Constraints/Tests.BehaviorShouldConstrain_A.cs | Disables new IHR0020 in constraint functional tests where mismatch is intentional. |
| tests/Immediate.Handlers.FunctionalTests/Behavior/Constraints/Tests.BehaviorShouldConstrain_B.cs | Disables new IHR0020 in constraint functional tests where mismatch is intentional. |
| tests/Immediate.Handlers.FunctionalTests/Behavior/Constraints/Tests.BehaviorShouldConstrain_C.cs | Disables new IHR0020 in constraint functional tests where mismatch is intentional. |
| tests/Immediate.Handlers.FunctionalTests/Behavior/Constraints/Tests.BehaviorShouldConstrain_D.cs | Disables new IHR0020 in constraint functional tests where mismatch is intentional. |
| src/Immediate.Handlers.Generators/Utility.cs | Removes generator-local Utility (migrated/shared via src/Common). |
| src/Immediate.Handlers.Generators/TransformHandler.cs | Uses new return-type helpers; adjusts base-type capture to original definitions. |
| src/Immediate.Handlers.Generators/TransformBehaviors.cs | Accepts 0–2 type parameters; extracts/serializes constraint info for pipeline filtering and rendering. |
| src/Immediate.Handlers.Generators/Templates/Handler.sbntxt | Updates handler template to use a precomputed behavior “type_name” instead of forcing <TReq,TRes>. |
| src/Immediate.Handlers.Generators/Models.cs | Refactors behavior constraint representation for generator filtering/rendering. |
| src/Immediate.Handlers.Generators/ImmediateHandlersGenerator.cs | Filters behaviors via structured constraints; generates correct behavior type arguments for 0/1/2-parameter behaviors. |
| src/Immediate.Handlers.CodeFixes/StaticToSealedHandlerCodeFixProvider.cs | Updates attribute checks to use new extension properties. |
| src/Immediate.Handlers.CodeFixes/Immediate.Handlers.CodeFixes.csproj | Adds Microsoft.Bcl.HashCode package reference. |
| src/Immediate.Handlers.Analyzers/Properties/launchSettings.json | Updates DebugRoslynComponent targets (adds functional tests target). |
| src/Immediate.Handlers.Analyzers/InvalidIHandlerAnalyzer.cs | Updates extension usage (IsIHandler property). |
| src/Immediate.Handlers.Analyzers/Immediate.Handlers.Analyzers.csproj | Adds Microsoft.Bcl.HashCode package reference. |
| src/Immediate.Handlers.Analyzers/HandlerClassAnalyzer.cs | Adds IHR0020 and handler-level behavior type compatibility analysis. |
| src/Immediate.Handlers.Analyzers/DiagnosticIds.cs | Adds IDs for IHR0020 and IHR0021. |
| src/Immediate.Handlers.Analyzers/BehaviorsClassAnalyzer.cs | New analyzer enforcing supported generic constraints on behavior type parameters (IHR0021). |
| src/Immediate.Handlers.Analyzers/BehaviorsAnalyzer.cs | Allows behavior types with 0–2 generic parameters; keeps unbound generic enforcement for generic types. |
| src/Immediate.Handlers.Analyzers/AnalyzerReleases.Unshipped.md | Adds unshipped analyzer release tracking file (currently empty). |
| src/Immediate.Handlers.Analyzers/AnalyzerReleases.Shipped.md | Documents new analyzer rules in release notes (IHR0020/IHR0021). |
| src/Common/Utility.cs | Adds shared utility extensions (NullIf, WhereNotNull). |
| src/Common/ITypeSymbolExtensions.cs | Adds behavior constraint extraction + constraint satisfaction helpers; refactors existing symbol helpers into extension properties. |
| src/Common/BehaviorConstraintInfo.cs | Adds shared constraint model used by analyzers/generator for behavior compatibility. |
| Directory.Packages.props | Bumps DI package versions and adds diagnostics testing package version. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...zerTests/HandlerClassAnalyzerTests/Tests.HandlerUsesBehaviorTypeWithZeroGenericParameters.cs
Show resolved
Hide resolved
src/Immediate.Handlers.Generators/ImmediateHandlersGenerator.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 52 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
src/Immediate.Handlers.Analyzers/AnalyzerReleases.Unshipped.md:2
AnalyzerReleases.Unshipped.mdwas added but is currently empty/blank. If release tracking is being used, new rules should typically be recorded inUnshippeduntil a version is actually released, and empty files tend to cause confusion. Either add the appropriate entries or remove the empty file if it’s not needed.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 52 out of 52 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/Immediate.Handlers.Analyzers/AnalyzerReleases.Unshipped.md:2
- The analyzer release tracking files look inconsistent:
AnalyzerReleases.Unshipped.mdis effectively empty, butAnalyzerReleases.Shipped.mdalready lists IHR0020/IHR0021 under a new "Release 3.2" section. Unless these diagnostics have actually shipped, they should be documented inAnalyzerReleases.Unshipped.mdinstead (and only moved to Shipped as part of a release).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| constraints.Add( | ||
| baseConstraints[index].Item2.ExactType != null | ||
| ? baseConstraints[index] | ||
| : (name, new() { ExactType = baseType.TypeArguments[index], TypeConstraints = [] }) | ||
| ); |
There was a problem hiding this comment.
AddMissingConstraint sets ExactType = baseType.TypeArguments[index] when the base Behavior<,> argument is a type parameter. That makes ExactType become something like TRequest/TResponse, which will never equal a concrete handler request/response type, causing IHR0020 to fire incorrectly and behaviors to be filtered out even when they should match (e.g., behaviors that forward a single type parameter through the base Behavior). Consider treating ITypeParameterSymbol arguments as "parameterized" (leave ExactType null and carry constraints), and only set ExactType when the base argument is a non-type-parameter symbol.
| constraints.Add( | |
| baseConstraints[index].Item2.ExactType != null | |
| ? baseConstraints[index] | |
| : (name, new() { ExactType = baseType.TypeArguments[index], TypeConstraints = [] }) | |
| ); | |
| var baseConstraint = baseConstraints[index]; | |
| var baseConstraintInfo = baseConstraint.Item2; | |
| if (baseConstraintInfo.ExactType is not null) | |
| { | |
| constraints.Add(baseConstraint); | |
| return; | |
| } | |
| var baseTypeArgument = baseType.TypeArguments[index]; | |
| if (baseTypeArgument is ITypeParameterSymbol typeParameterSymbol) | |
| { | |
| // Treat type-parameter arguments as parameterized: do not set ExactType, | |
| // but carry over the parameter's own type constraints. | |
| constraints.Add( | |
| (name, new() { ExactType = null, TypeConstraints = typeParameterSymbol.ConstraintTypes }) | |
| ); | |
| } | |
| else | |
| { | |
| // For non-type-parameter arguments, we can treat the argument as the exact type. | |
| constraints.Add( | |
| (name, new() { ExactType = baseTypeArgument, TypeConstraints = [] }) | |
| ); | |
| } |
| foreach (var attribute in method.ContainingSymbol.GetAttributes()) | ||
| { | ||
| if (attribute is not | ||
| { | ||
| AttributeClass.IsBehaviorsAttribute: true, | ||
| ConstructorArguments: [{ Kind: TypedConstantKind.Array } behaviors] | ||
| }) |
There was a problem hiding this comment.
IHR0020 validation only scans [Behaviors] attributes on the handler's containing type (method.ContainingSymbol.GetAttributes()), but the generator also applies assembly-level [assembly: Behaviors(...)] attributes. This means mismatched fixed/partially-specified behaviors declared at assembly scope won't be diagnosed. Consider also scanning context.Compilation.Assembly.GetAttributes() (and/or modules) for BehaviorsAttribute and reporting IHR0020 at the corresponding assembly attribute argument location.
Behaviors previously required exactly 2 generic type parameters. This restriction is now lifted to support 0, 1, or 2 parameters, allowing behaviors with fixed or partially-specified types.
Changes
Analyzer (
BehaviorsAnalyzer.cs)Generator (
TransformBehaviors.cs,ImmediateHandlersGenerator.cs,Models.cs)Template (
Handler.sbntxt)TypeParameterCountBehavior,Behavior<TRequest>, andBehavior<TRequest, TResponse>signaturesExample
Generator constructs behavior instances with handler's request/response types, filtering incompatible behaviors via type matching in the pipeline. The analyzer now validates that behaviors with fixed type arguments match the handler's request/response types, reporting IHR0020 errors for mismatches.
Tests
Analyzer Tests (
Tests.BehaviorTypeWithOneGenericParameter.cs)BehaviorTypeWithCorrectTRequest_DoesNotAlert: Validates single-parameter behaviors with correct request type don't trigger errorsBehaviorTypeWithIncorrectTRequest_Alerts: Validates IHR0020 diagnostic for request type mismatchesBehaviorTypeWithCorrectTResponse_DoesNotAlert: Validates single-parameter behaviors with correct response type don't trigger errorsBehaviorTypeWithIncorrectTResponse_Alerts: Validates IHR0020 diagnostic for response type mismatchesGenerator Tests (
BehaviorTests.cs,InvalidBehaviorsTests.cs)ZeroTypeParameterBehavior: Validates non-generic behaviors generate correct handler codeRequestTypeParameterBehavior: Validates single-parameter behaviors (request position) generate correct codeResponseTypeParameterBehavior: Validates single-parameter behaviors (response position) generate correct codeThreeGenericParameterBehaviorShouldProduceNothing: Validates behaviors with 3+ parameters produce no codeFunctional Tests (
PartiallySpecifiedBehaviorTests.cs)Status
Work in Progress: Single-parameter behavior code generation is partially implemented. The analyzer and type detection logic are complete, but the template generation needs additional work to properly construct single-parameter behaviors with the correct type argument position.
Current test status: 9 tests failing, all related to single-parameter behavior code generation.
Original prompt
Behaviors to be partially or fully specified types #288💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.