Skip to content

feat: Add support for pure open-generic handler registration#405

Merged
samtrion merged 2 commits into
mainfrom
feature/sourcegenerator-open-generic-handlers
Apr 16, 2026
Merged

feat: Add support for pure open-generic handler registration#405
samtrion merged 2 commits into
mainfrom
feature/sourcegenerator-open-generic-handlers

Conversation

@samtrion

@samtrion samtrion commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Introduce [PulseGenericHandler] for open-generic DI registration via typeof()-based syntax. Update generator logic, HandlerRegistration, and WellKnownTypeNames to support and track open-generic handlers. Extend documentation and tests to cover new attribute usage and ensure correct diagnostics. Add PulseGenericHandlerAttribute.cs.

Summary by CodeRabbit

  • New Features

    • Added support for open-generic handler registration via a new attribute, with configurable service lifetime (Scoped default).
  • Documentation

    • Updated docs with examples, generated registration snippets, and a comparison of handler registration approaches, including open-generic guidance.
  • Tests

    • Added unit tests and verified snapshots covering open-generic registrations and lifetime variations.
  • Bug Fixes

    • Suppressed spurious diagnostics for recognized open-generic handlers.

@samtrion samtrion self-assigned this Apr 16, 2026
@samtrion
samtrion requested a review from a team as a code owner April 16, 2026 07:29
@samtrion
samtrion requested a review from Hnogared April 16, 2026 07:29
@samtrion samtrion added state:ready for merge Indicates that a pull request has been reviewed and approved, and is ready to be merged into the mai type:feature Indicates a new feature or enhancement to be added. labels Apr 16, 2026
@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@samtrion has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 49 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 20 minutes and 49 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 43d52150-67af-4edd-b16f-f74d5548ab98

📥 Commits

Reviewing files that changed from the base of the PR and between 10aef38 and 69c4f83.

📒 Files selected for processing (39)
  • src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenCommandHandlerAnnotatedThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenCommandHandlerVoidAnnotatedThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenConcreteHandlerAnnotatedWithGenericAttributeThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenDottedAssemblyNameThenMethodNameRemovesDots.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenDuplicateCommandHandlersThenPulse002MessageContainsBothHandlerNames.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenDuplicateCommandHandlersThenPulse002Reported.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenDuplicateQueryHandlersThenPulse002Reported.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenDuplicateStreamQueryHandlersThenPulse002Reported.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenEmptyAssemblyNameThenFallbackMethodNameIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenEventHandlerAnnotatedThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenGeneratedCodeEmittedThenItContainsAutoGeneratedComment.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenGeneratedCodeEmittedThenItUsesFullyQualifiedServiceCollectionDescriptorExtensions.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenHandlersHaveDifferentLifetimesThenEachUsesItsOwn.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenMultipleEventHandlersForSameEventThenNoPulse002.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenMultipleHandlerClassesThenAllRegistered.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenMultipleInterfacesImplementedThenAllRegistered.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenNoRootNamespaceProvidedThenDefaultNamespaceIsUsed.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenOpenGenericCommandHandlerWithExplicitMessageTypeThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenOpenGenericEventHandlerWithExplicitMessageTypeThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenOpenGenericHandlerWithExplicitMessageTypeAndSingletonLifetimeThenSingletonIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenOpenGenericHandlerWithMultipleExplicitMessageTypesThenAllRegistered.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPrioritizedEventHandlerAnnotatedThenRegisteredAsIEventHandler.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericEventHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericQueryHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericQueryHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericStreamQueryHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericStreamQueryHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenQueryHandlerAnnotatedThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenRecordCommandHandlerAnnotatedThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenRootNamespaceProvidedThenGeneratedCodeUsesIt.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenScopedLifetimeExplicitlySpecifiedThenTryAddScopedIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenSingleHandlerAndMultiInterfaceHandlerAndMultipleOpenGenericHandlersThenAllRegistered.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenSingletonLifetimeSpecifiedThenTryAddSingletonIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenStreamQueryHandlerAnnotatedThenRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenTransientLifetimeSpecifiedThenTryAddTransientIsGenerated.verified.txt

Walkthrough

Adds a new [PulseGenericHandler] attribute and source-generator support to emit open-generic DI registrations using typeof(...); updates the handler registration model, generator pipelines, docs, and unit tests/snapshots.

Changes

Cohort / File(s) Summary
Extensibility Attribute
src/NetEvolve.Pulse.Extensibility/Attributes/PulseGenericHandlerAttribute.cs
Adds sealed PulseGenericHandlerAttribute (class-only, non-inheritable, single-use) with public PulseServiceLifetime Lifetime { get; set; } defaulting to Scoped for generator consumption.
Source Generator
src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs
Adds incremental pipeline to collect open-generic handler infos, ExtractPureGenericHandlerInfo, BuildOpenGenericHandlerRegistrations, GetOpenGenericTypeName; merges generic results into flattened collection; treats [PulseGenericHandler] as annotated and emits services.TryAdd{Lifetime}(typeof(service), typeof(handler)) for open-generic groups.
Well-Known Names
src/NetEvolve.Pulse.SourceGeneration/WellKnownTypeNames.cs
Adds PulseGenericHandlerAttributeFullName constant for the new attribute.
Model
src/NetEvolve.Pulse.SourceGeneration/Models/HandlerRegistration.cs
Adds public bool IsOpenGeneric { get; }, extends constructor with optional isOpenGeneric parameter, and includes the flag in equality and hash-code logic.
Documentation
src/NetEvolve.Pulse.Extensibility/README.md, src/NetEvolve.Pulse.SourceGeneration/README.md
Documents [PulseGenericHandler], shows open-generic usage and emitted typeof(...) DI registration patterns, and updates mapping/diagnostic guidance for handler attributes.
Tests & Snapshots
tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs, tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/*WhenPureGeneric*.verified.txt
Adds four unit tests and verified snapshots asserting open-generic registration output for generic command/event handlers, lifetime variations (scoped/singleton), and suppression of PULSE003 for unannotated open-generic types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble code in dewy light,
A generic hop, a registration bright,
Attribute sewn, lifetimes set,
typeof() doors swing open yet—
Hoppity joy, the DI field's just right! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: introduction of support for pure open-generic handler registration via the new [PulseGenericHandler] attribute.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs (1)

423-444: ⚠️ Potential issue | 🟠 Major

Don't suppress diagnostics for closed types annotated with [PulseGenericHandler].

ExtractPureGenericHandlerInfo returns null for non-generic classes at Lines 363-365. With this unconditional skip, a closed handler annotated by mistake with [PulseGenericHandler] is neither registered nor diagnosed. Please only treat [PulseGenericHandler] as "handled" when the target is actually open generic, or emit a dedicated invalid-usage diagnostic.

Suggested fix
-                        || string.Equals(
-                            GetFullMetadataName(attr.AttributeClass),
-                            PulseGenericHandlerAttributeFullName,
-                            StringComparison.Ordinal
-                        )
+                        || (
+                            classSymbol.TypeParameters.Length > 0
+                            && string.Equals(
+                                GetFullMetadataName(attr.AttributeClass),
+                                PulseGenericHandlerAttributeFullName,
+                                StringComparison.Ordinal
+                            )
+                        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs`
around lines 423 - 444, The skip logic in PulseHandlerGenerator.cs currently
treats any class annotated with PulseGenericHandlerAttributeFullName as
"handled" even for closed/non-generic classes, which hides misuse; update the
attribute-check in the classSymbol.GetAttributes().Any(...) branch so that
PulseGenericHandlerAttributeFullName only causes a skip if
ExtractPureGenericHandlerInfo(classSymbol) returns non-null (i.e., the class is
an open generic), and if ExtractPureGenericHandlerInfo returns null instead emit
the dedicated invalid-usage diagnostic (use the existing diagnostic reporting
flow used elsewhere in PulseHandlerGenerator), while leaving the existing
handling for PulseHandlerAttributeFullName and
PulseHandlerGenericAttributeFullName unchanged.
🧹 Nitpick comments (1)
tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs (1)

1140-1228: Consider adding pure-generic query/stream-query coverage for parity.

You now validate pure generic command/event paths; adding IQueryHandler<TQuery, TResult> and IStreamQueryHandler<TQuery, TResult> pure-generic cases would close the remaining handler-kind gap and reduce regression risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs`
around lines 1140 - 1228, Add unit tests mirroring the existing pure-generic
command/event cases to cover query and stream-query handlers: create tests like
WhenPureGenericQueryHandlerAnnotatedThenOpenGenericRegistrationIsGenerated and
WhenPureGenericStreamQueryHandlerAnnotatedThenOpenGenericRegistrationIsGenerated
that use source snippets defining GenericQueryHandler<TQuery, TResult> :
IQueryHandler<TQuery, TResult> and GenericStreamQueryHandler<TQuery, TResult> :
IStreamQueryHandler<TQuery, TResult> (with and without [PulseGenericHandler],
and a variant using [PulseGenericHandler(Lifetime =
PulseServiceLifetime.Singleton)]), then call RunGenerator(...) and Await
VerifySources(...) just like the existing tests for GenericCommandHandler and
GenericEventHandler to ensure parity and close the handler-kind gap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs`:
- Around line 1015-1037: BuildOpenGenericHandlerRegistrations is currently
emitting open-generic DI registrations for every original interface without
checking that the handler's type parameters align exactly with the interface's
type arguments; add an IsPureOpenGenericMapping(INamedTypeSymbol handler,
INamedTypeSymbol iface) helper that returns true only when
handler.TypeParameters.Length == iface.TypeArguments.Length and each
iface.TypeArguments[i] is the corresponding handler.TypeParameters[i] (use
SymbolEqualityComparer.Default for comparison), then call this helper before
adding a HandlerRegistration (when using GetOpenGenericTypeName,
TryGetHandlerKind and creating HandlerRegistration) and skip/ignore interfaces
that do not pass the check so mismatched arity/position cases are not emitted.

In
`@tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt`:
- Around line 13-21: The XML documentation generated for
PulseRegistrationExtensions incorrectly references only [PulseHandler]; update
the generator template producing the summary text (used in the GeneratedCode
class PulseRegistrationExtensions and the method-level summary) to either
mention both attributes ([PulseHandler] and [PulseGenericHandler]) or use
neutral wording like "Pulse handler attributes" or "Pulse handlers" so the docs
accurately reflect handlers created from PulseGenericHandler as well.

---

Outside diff comments:
In `@src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs`:
- Around line 423-444: The skip logic in PulseHandlerGenerator.cs currently
treats any class annotated with PulseGenericHandlerAttributeFullName as
"handled" even for closed/non-generic classes, which hides misuse; update the
attribute-check in the classSymbol.GetAttributes().Any(...) branch so that
PulseGenericHandlerAttributeFullName only causes a skip if
ExtractPureGenericHandlerInfo(classSymbol) returns non-null (i.e., the class is
an open generic), and if ExtractPureGenericHandlerInfo returns null instead emit
the dedicated invalid-usage diagnostic (use the existing diagnostic reporting
flow used elsewhere in PulseHandlerGenerator), while leaving the existing
handling for PulseHandlerAttributeFullName and
PulseHandlerGenericAttributeFullName unchanged.

---

Nitpick comments:
In
`@tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs`:
- Around line 1140-1228: Add unit tests mirroring the existing pure-generic
command/event cases to cover query and stream-query handlers: create tests like
WhenPureGenericQueryHandlerAnnotatedThenOpenGenericRegistrationIsGenerated and
WhenPureGenericStreamQueryHandlerAnnotatedThenOpenGenericRegistrationIsGenerated
that use source snippets defining GenericQueryHandler<TQuery, TResult> :
IQueryHandler<TQuery, TResult> and GenericStreamQueryHandler<TQuery, TResult> :
IStreamQueryHandler<TQuery, TResult> (with and without [PulseGenericHandler],
and a variant using [PulseGenericHandler(Lifetime =
PulseServiceLifetime.Singleton)]), then call RunGenerator(...) and Await
VerifySources(...) just like the existing tests for GenericCommandHandler and
GenericEventHandler to ensure parity and close the handler-kind gap.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd4abef5-3ba1-4095-a4bb-7368ac990dd7

📥 Commits

Reviewing files that changed from the base of the PR and between bb34053 and 83a4c74.

📒 Files selected for processing (11)
  • src/NetEvolve.Pulse.Extensibility/Attributes/PulseGenericHandlerAttribute.cs
  • src/NetEvolve.Pulse.Extensibility/README.md
  • src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs
  • src/NetEvolve.Pulse.SourceGeneration/Models/HandlerRegistration.cs
  • src/NetEvolve.Pulse.SourceGeneration/README.md
  • src/NetEvolve.Pulse.SourceGeneration/WellKnownTypeNames.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericEventHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerNotAnnotatedThenNoPulse003.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt

@codecov

codecov Bot commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.28169% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.76%. Comparing base (380596b) to head (69c4f83).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...urceGeneration/Generators/PulseHandlerGenerator.cs 84.84% 4 Missing and 6 partials ⚠️
...lse.SourceGeneration/Models/HandlerRegistration.cs 25.00% 3 Missing ⚠️
...ibility/Attributes/PulseGenericHandlerAttribute.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #405      +/-   ##
==========================================
- Coverage   91.98%   91.76%   -0.23%     
==========================================
  Files         139      140       +1     
  Lines        5266     5330      +64     
  Branches      484      498      +14     
==========================================
+ Hits         4844     4891      +47     
- Misses        282      294      +12     
- Partials      140      145       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@samtrion
samtrion force-pushed the feature/sourcegenerator-open-generic-handlers branch from cb4d0ec to 8819321 Compare April 16, 2026 11:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt (1)

13-21: ⚠️ Potential issue | 🟡 Minor

XML docs still reference the wrong attribute for this scenario.

Line 14 and Line 20 still mention only <c>[PulseHandler]</c>, but this snapshot is produced from [PulseGenericHandler]. Please fix the generator template wording to mention both attributes or use neutral wording (e.g., “Pulse handlers”).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt`
around lines 13 - 21, The XML documentation in the generated
PulseRegistrationExtensions class incorrectly references only
"<c>[PulseHandler]</c>" even when generation was driven by
"[PulseGenericHandler]"; update the source generator template that emits the
summary (the text around the GeneratedCode("NetEvolve.Pulse.SourceGeneration",
"{version}") and the class-level docs for PulseRegistrationExtensions) to either
mention both attributes (e.g., "<c>[PulseHandler]</c> and
<c>[PulseGenericHandler]</c>") or use neutral wording like "Pulse handlers" so
the docs match all originating attributes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt`:
- Around line 13-21: The XML documentation in the generated
PulseRegistrationExtensions class incorrectly references only
"<c>[PulseHandler]</c>" even when generation was driven by
"[PulseGenericHandler]"; update the source generator template that emits the
summary (the text around the GeneratedCode("NetEvolve.Pulse.SourceGeneration",
"{version}") and the class-level docs for PulseRegistrationExtensions) to either
mention both attributes (e.g., "<c>[PulseHandler]</c> and
<c>[PulseGenericHandler]</c>") or use neutral wording like "Pulse handlers" so
the docs match all originating attributes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 830633d7-9d4b-49d6-a665-d6b395469904

📥 Commits

Reviewing files that changed from the base of the PR and between 83a4c74 and 8819321.

📒 Files selected for processing (11)
  • src/NetEvolve.Pulse.Extensibility/Attributes/PulseGenericHandlerAttribute.cs
  • src/NetEvolve.Pulse.Extensibility/README.md
  • src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs
  • src/NetEvolve.Pulse.SourceGeneration/Models/HandlerRegistration.cs
  • src/NetEvolve.Pulse.SourceGeneration/README.md
  • src/NetEvolve.Pulse.SourceGeneration/WellKnownTypeNames.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericEventHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerNotAnnotatedThenNoPulse003.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt
✅ Files skipped from review due to trivial changes (6)
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerNotAnnotatedThenNoPulse003.verified.txt
  • src/NetEvolve.Pulse.SourceGeneration/WellKnownTypeNames.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs
  • src/NetEvolve.Pulse.Extensibility/Attributes/PulseGenericHandlerAttribute.cs
  • src/NetEvolve.Pulse.SourceGeneration/README.md
  • src/NetEvolve.Pulse.Extensibility/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/NetEvolve.Pulse.SourceGeneration/Models/HandlerRegistration.cs
  • src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs

Introduce [PulseGenericHandler] for open-generic DI registration via typeof()-based syntax. Update generator logic, HandlerRegistration, and WellKnownTypeNames to support and track open-generic handlers. Extend documentation and tests to cover new attribute usage and ensure correct diagnostics. Add PulseGenericHandlerAttribute.cs.
@samtrion
samtrion force-pushed the feature/sourcegenerator-open-generic-handlers branch from 8819321 to 10aef38 Compare April 16, 2026 12:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt (1)

13-15: ⚠️ Potential issue | 🟡 Minor

Generated XML docs still exclude [PulseGenericHandler] scenarios.

Line 14 and Line 20 mention only <c>[PulseHandler]</c>, but this snapshot is produced from [PulseGenericHandler]. Update the generator template wording (neutral “Pulse handlers” or both attributes) and regenerate snapshots.

Suggested wording update
-/// Auto-generated extension method to register Pulse handlers annotated
-/// with <c>[PulseHandler]</c> inside the Assembly <c>TestAssembly</c>.
+/// Auto-generated extension method to register Pulse handlers discovered
+/// inside the Assembly <c>TestAssembly</c>.
...
-/// Registers all <c>[PulseHandler]</c>-annotated handlers from the assembly <c>TestAssembly</c> into the DI container.
+/// Registers all discovered Pulse handlers from the assembly <c>TestAssembly</c> into the DI container.

Also applies to: 20-21

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt`
around lines 13 - 15, The XML doc template used to generate the snapshot
incorrectly references only <c>[PulseHandler]</c> even when the test
(PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated)
is driven by <c>[PulseGenericHandler]</c>; update the generator template text to
use neutral wording like "Pulse handlers" or explicitly include both
<c>[PulseHandler]</c> and <c>[PulseGenericHandler]</c> (look for the
generator/template method that emits the summary for handler registration), then
regenerate the snapshots so the verified snapshot
_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
reflects the corrected wording.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt`:
- Around line 13-15: The XML doc template used to generate the snapshot
incorrectly references only <c>[PulseHandler]</c> even when the test
(PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated)
is driven by <c>[PulseGenericHandler]</c>; update the generator template text to
use neutral wording like "Pulse handlers" or explicitly include both
<c>[PulseHandler]</c> and <c>[PulseGenericHandler]</c> (look for the
generator/template method that emits the summary for handler registration), then
regenerate the snapshots so the verified snapshot
_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
reflects the corrected wording.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ceb2bdc4-c52d-4886-92f0-cb587173e1dc

📥 Commits

Reviewing files that changed from the base of the PR and between 8819321 and 10aef38.

📒 Files selected for processing (11)
  • src/NetEvolve.Pulse.Extensibility/Attributes/PulseGenericHandlerAttribute.cs
  • src/NetEvolve.Pulse.Extensibility/README.md
  • src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs
  • src/NetEvolve.Pulse.SourceGeneration/Models/HandlerRegistration.cs
  • src/NetEvolve.Pulse.SourceGeneration/README.md
  • src/NetEvolve.Pulse.SourceGeneration/WellKnownTypeNames.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericEventHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerAnnotatedThenOpenGenericRegistrationIsGenerated.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerNotAnnotatedThenNoPulse003.verified.txt
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerWithSingletonLifetimeThenSingletonOpenGenericRegistrationIsGenerated.verified.txt
✅ Files skipped from review due to trivial changes (6)
  • src/NetEvolve.Pulse.SourceGeneration/WellKnownTypeNames.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/_snapshots/PulseHandlerGeneratorTests.WhenPureGenericHandlerNotAnnotatedThenNoPulse003.verified.txt
  • src/NetEvolve.Pulse.Extensibility/README.md
  • src/NetEvolve.Pulse.SourceGeneration/README.md
  • src/NetEvolve.Pulse.Extensibility/Attributes/PulseGenericHandlerAttribute.cs
  • tests/NetEvolve.Pulse.SourceGeneration.Tests.Unit/PulseHandlerGeneratorTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NetEvolve.Pulse.SourceGeneration/Generators/PulseHandlerGenerator.cs

Update PulseHandler generator to better support [PulseGenericHandler] for open generics. Only skip [PulseHandler] and [PulseHandler<T>] for closed types, ensuring correct diagnostics for [PulseGenericHandler] misuse. Add IsPureOpenGenericMapping to restrict open generic registrations to pure positional mappings. Update generated docs to mention both handler attributes. Add and update tests and verified outputs for new registration logic.
@samtrion
samtrion merged commit 93f92a4 into main Apr 16, 2026
10 checks passed
@samtrion
samtrion deleted the feature/sourcegenerator-open-generic-handlers branch April 16, 2026 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state:ready for merge Indicates that a pull request has been reviewed and approved, and is ready to be merged into the mai type:feature Indicates a new feature or enhancement to be added.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant