DI027
Rx Subscription On Longer-Lived Observable Without Dispose
The Rx twin of DI025. IObservable<T>.Subscribe(...) returns an IDisposable token that unsubscribes the observer when disposed, so there is no -= to prove missing — the leak proof inverts to a discarded token. A transient or scoped registered service subscribes an instance-capturing handler (method group, this-capturing lambda, or stored delegate) to an observable exposed by a longer-lived publisher — an injected singleton dependency, or a scoped publisher shared by a transient subscriber — and discards the returned token. The observable is reached through DI025's classified receivers (an injected member proven ctor-assigned, a constructor parameter, or a stable chained projection such as _source.Ticks), and publisher lifetime resolution follows the same rules (most conservative registration wins, closed registrations preferred over open-generic fallbacks, keyed-only registrations excluded). Matching is FQN-light: any method named Subscribe returning System.IDisposable, invoked on a System.IObservable<T> receiver, so System.Reactive, community Rx, and hand-rolled extensions all bind.
Warning Default severity · Code fix: No
Guardrails
When DI027 stays silent
DI027 fires only on the highest-confidence discard shapes — an ignored expression statement, a discard assignment (_ = obs.Subscribe(H)), a local initialized with the token or assigned it in a standalone statement and never otherwise referenced (and not a using declaration), or a simple assignment to a private field declared on the subscriber when that field has no other symbol-bound reference across any partial declaration. An assignment expression consumed by return, using, an argument, or another expression stays silent, as does a later disposal, return, argument pass, reassignment, or other field access; inherited and public/internal/protected fields also stay silent because external handling cannot be ruled out. A direct static readonly reference-type observable field counts as a process-lifetime publisher only when its declaration initializes it exactly once with an object creation through an identity or implicit reference conversion; mutating a member of that publisher or creating a ref readonly local alias does not change its identity. Mutable, value-type, null-initialized, static-constructor-assigned or reassigned fields—including deconstruction, compound, and increment/decrement writes—writable ref/out argument or ref-local escapes, null-producing conversions, and static properties remain conservative. using/using var, CompositeDisposable/DisposeWith/AddTo/SerialDisposable, and more complex field flows remain conservative. DI025's silence-on-unknown legs all apply: singleton subscribers, transient publishers, scoped-on-scoped pairs, static or this-free lambdas, separate observer objects, unregistered subscriber/publisher types, keyed-only publishers, unstable chained projections, non-extension static helpers named Subscribe, and non-observer Subscribe(this) overloads.