fix(extractor): resolve-aware ignored-Subscribe — only flag when Subscribe returns IDisposable (mined StackExchange.Redis)#93
Conversation
…cribe returns IDisposable (mined StackExchange.Redis) WPF004 flagged any bare member `x.Subscribe(...)` as an ignored IDisposable subscription token. But that token only exists for the Rx shape (`IObservable<T>.Subscribe()` -> IDisposable). StackExchange.Redis's `ISubscriber.Subscribe(channel, handler, flags)` returns VOID — there is nothing to dispose — so flagging it (ConnectionMultiplexer.Sentinel, twice) was a false positive, and the same applies to the many event-bus `Subscribe(handler)` APIs. Make it resolve-aware (mirrors IsOwnedDisposableType / #83): require the call's return type to implement IDisposable; a RESOLVED void / non-IDisposable return is silenced, while an UNRESOLVED return keeps the syntactic benefit of the doubt (so ReactiveUI's WhenAnyValue chains, whose type doesn't bind on the Linux runner, still fire). Regression sample VoidSubscribeSample.cs: VoidSubscriber (void Subscribe) is SILENT; the control DisposableSubscriber (IDisposable-returning Subscribe, ignored) still WARNs. The existing IDisposable control (MessengerViewModel. InboxViewModel) is unchanged. Mined: StackExchange.Redis (first fresh target after the Npgsql arc). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds a ChangesResolve-aware void-Subscribe suppression (WPF004)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b2d818a615
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // return type keeps the syntactic benefit of the doubt (mirrors IsOwnedDisposableType, #83). Mined: | ||
| // StackExchange.Redis ConnectionMultiplexer.Sentinel `sub.Subscribe(channel, handler, FireAndForget)`. | ||
| static bool SubscribeResultIsDisposable(ITypeSymbol? rt) => | ||
| rt is null or IErrorTypeSymbol || ImplementsIDisposable(rt); |
There was a problem hiding this comment.
Treat dynamic Subscribe results as unresolved
When the receiver is dynamic, Roslyn gives x.Subscribe(...) a dynamic return type, which is neither null/IErrorTypeSymbol nor an IDisposable interface implementation. This now suppresses ignored dynamic Subscribe calls even though the extractor cannot prove the runtime overload is void or non-disposable, so dynamic Rx/event-bus subscriptions that used to be reported disappear; treat TypeKind.Dynamic as unknown here unless the call is proven non-IDisposable.
Useful? React with 👍 / 👎.
…nced (Codex) A `dynamic` receiver gives `x.Subscribe(...)` a dynamic return type, which is neither null/IErrorTypeSymbol nor an IDisposable implementation — so the new resolve-aware gate wrongly silenced ignored dynamic Subscribe calls even though the runtime overload can't be proven void/non-disposable. Treat TypeKind.Dynamic as unresolved (benefit of the doubt -> still flag), alongside null/error returns. Regression: VoidSubscribeSample gains DynamicSubscriber (a `dynamic bus.Subscribe(handler)`) which must STILL warn. VoidSubscriber stays silent; DisposableSubscriber still warns. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
| { | ||
| public DisposableSubscriber(IObservableBus bus) | ||
| { | ||
| bus.Subscribe(_ => { }); // returns IDisposable, ignored -> WARN (resolve-aware still fires) |
| public DynamicSubscriber(dynamic bus) | ||
| { | ||
| Action<object> handler = _ => { }; | ||
| bus.Subscribe(handler); // dynamic return -> unknown -> WARN |
First fix from the fresh StackExchange.Redis mine.
The FP
WPF004 flagged any bare member
x.Subscribe(...)as an ignoredIDisposablesubscription token. But that token only exists for the Rx shape —IObservable<T>.Subscribe()→IDisposable. StackExchange.Redis's handler overload:is
ISubscriber.Subscribe(RedisChannel, Action<…>, CommandFlags)→void. There's nothing to dispose, so flagging it (twice, inConnectionMultiplexer.Sentinel) was a false positive — and the same applies to the many event-busSubscribe(handler)APIs. (Verified against the Redis source.)The fix
Make it resolve-aware (mirrors
IsOwnedDisposableType/ #83): require the call's return type to implementIDisposable.void/ non-IDisposablereturn → silenced (Redis, event buses)IDisposablereturn → still flagged (Rx,IMessenger.Subscribe)WhenAnyValue(...).Subscribe(...)(whose type doesn't bind on the Linux runner) still firesRegression sample (
VoidSubscribeSample.cs)VoidSubscriber(voidSubscribe) → silentDisposableSubscriber(IDisposable-returningSubscribe, ignored) → still warnsThe existing IDisposable controls (
MessengerViewModel.InboxViewModelwarns;CleanInboxViewModelcaptures+disposes → silent) are unchanged.🤖 Generated with Claude Code
Generated by Claude Code
Summary by CodeRabbit
Bug Fixes
Subscribe(...)patterns by correctly handling void-returning/unknown-return shapes versusIDisposable-returning ones, reducing false positives while preserving expected warnings where a disposable subscription token can be produced.Tests
Subscribescenario and to verify continued correct behavior for bothIDisposable-returning and dynamic/unknown cases.Documentation