cancel-subscriptions
Same as cancel_subscriptions in Dart lints
- Group
- Correctness
- Path
lint/correctness/cancel-subscriptions- Default
- On (recommended)
- Origin
- Dart lints
- Rule
- 197 of 512
Cancel instances of dart:async StreamSubscription.
A subscription that is never cancelled keeps its stream (and everything the listener captures) alive, leaking memory and running callbacks after their owner is gone:
class Watcher {
StreamSubscription<int>? _sub; // BAD: never cancelled
}
class Watcher {
StreamSubscription<int>? _sub;
void dispose() => _sub?.cancel(); // GOOD
}
Fields and local variables whose type provably implements
StreamSubscription are reported unless the file (fields) or enclosing
function body (locals) holds an official valid-use/escape case. Constructor
arguments and assignments into another local do not escape; references to
shadowing variables never cancel the original. Unresolved types stay silent.
Invalid
import 'dart:async';
class Watcher {
StreamSubscription<int>? _subscription;
late StreamSubscription<String> other;
void start(Stream<int> stream) {
_subscription = stream.listen((_) {});
}
}
void listenLocally(Stream<int> stream) {
final subscription = stream.listen((_) {});
subscription.pause();
}
void typedLocal(Stream<int> stream) {
StreamSubscription<int> subscription = stream.listen(print);
subscription.resume();
}
void fromController(StreamController<int> controller) {
var subscription = controller.stream.listen((_) {});
print(subscription.isPaused);
}
void inClosure(Stream<int> stream) {
final run = () {
final subscription = stream.listen((_) {});
subscription.pause();
};
run();
}
void aliasedDeclaration(Stream<int> stream) {
final subscription = stream.listen((_) {});
final alias = subscription;
alias.cancel();
}
void reassigned(Stream<int> stream, StreamSubscription<int>? holder) {
final subscription = stream.listen((_) {});
holder = subscription;
holder.cancel();
}
class SubscriptionBox {
final StreamSubscription<int> subscription;
SubscriptionBox(this.subscription);
}
void constructorArgument(Stream<int> stream) {
final subscription = stream.listen((_) {});
SubscriptionBox(subscription);
}
void functionArgument(Stream<int> stream, void Function(StreamSubscription<int>) callback) {
final subscription = stream.listen((_) {});
callback(subscription);
}
void assertedAlias(StreamSubscription<int>? source) {
final subscription = source!;
subscription.pause();
}
void shadowedClosure(Stream<int> stream) {
final subscription = stream.listen((_) {});
final release = (StreamSubscription<int> subscription) => subscription.cancel();
release(stream.listen((_) {}));
}
void localCascade(Stream<int> stream) {
final subscription = stream.listen((_) {})..pause();
}
class ThroughThisPause {
StreamSubscription<int>? subscription;
void hold() => this.subscription?.pause();
}
class LeakedField {
StreamSubscription<int>? subscription;
}
class ReleasedField {
StreamSubscription<int>? subscription;
void dispose() => subscription?.cancel();
}
class GenericSubscriptionBox<T> {
GenericSubscriptionBox.named(StreamSubscription<T> subscription);
}
void genericConstructorArgument(Stream<int> stream) {
final subscription = stream.listen((_) {});
GenericSubscriptionBox<int>.named(subscription);
}
class FactorySubscriptionBox<T> {
FactorySubscriptionBox._(StreamSubscription<T> subscription);
factory FactorySubscriptionBox(Stream<T> stream) {
final subscription = stream.listen((_) {});
return FactorySubscriptionBox<T>._(subscription);
}
}
void dynamicCallback(Stream<int> stream, dynamic callback) {
final subscription = stream.listen((_) {});
callback(subscription);
}
void assertedCallback(Stream<int> stream, void Function(StreamSubscription<int>)? callback) {
final subscription = stream.listen((_) {});
callback!(subscription);
}
void immediateCallback(Stream<int> stream) {
final subscription = stream.listen((_) {});
((StreamSubscription<int> value) {})(subscription);
}
void functionCallback(Stream<int> stream, Function callback) {
final subscription = stream.listen((_) {});
callback(subscription);
}
class CallbackBox {
CallbackBox(this.callback);
final void Function(StreamSubscription<int>) callback;
void pass(Stream<int> stream) {
final subscription = stream.listen((_) {});
this.callback(subscription);
}
}
void getterCallback(Stream<int> stream, CallbackBox box) {
final subscription = stream.listen((_) {});
box.callback(subscription);
}
abstract class ChainedSubscription implements StreamSubscription<int> {
StreamSubscription<int> next();
}
ChainedSubscription createSubscription() => throw StateError('unavailable');
void chainedCascadeRelease() {
final subscription = createSubscription();
subscription..next().cancel();
}
void indexedCallback(Stream<int> stream, List<void Function(StreamSubscription<int>)> callbacks) {
final subscription = stream.listen((_) {});
callbacks[0](subscription);
}
StreamSubscription<int> parenthesizedReturn(Stream<int> stream) {
final subscription = stream.listen((_) {});
return (subscription);
}
void parenthesizedCancel(Stream<int> stream) {
final subscription = stream.listen((_) {});
(subscription).cancel();
}
void parenthesizedInitializer(StreamSubscription<int> source) {
final subscription = (source);
}
class ExplicitFieldTransfer {
StreamSubscription<int>? transferredSubscription;
void start(Stream<int> stream) {
final local = stream.listen((_) {});
this.transferredSubscription = local;
}
void dispose() => transferredSubscription?.cancel();
}
void otherReceiverTransfer(Stream<int> stream, ReleasedField receiver) {
final subscription = stream.listen((_) {});
receiver.subscription = subscription;
}
class FieldCancelTearoff {
late StreamSubscription<int> subscription;
void retainCancel() {
final callback = subscription.cancel;
}
}
class OtherSubscriptionOwner {
StreamSubscription<int>? subscription;
void dispose() => subscription?.cancel();
}
class SameNameIsNotSameField {
StreamSubscription<int>? subscription;
void transfer(OtherSubscriptionOwner other, StreamSubscription<int> value) {
other.subscription = value;
}
}
extension type SubscriptionView(StreamSubscription<int> value) {}
class ErasedSubscriptionField {
SubscriptionView? subscription;
}
extension type SubscriptionInterface(StreamSubscription<int> value)
implements StreamSubscription<int> {}
void extensionSubscriptionLocal(Stream<int> stream) {
SubscriptionInterface subscription = SubscriptionInterface(stream.listen((_) {}));
}class Watcher { StreamSubscription<int>? _subscription; StreamSubscription<int>? _subscription; late StreamSubscription<String> other;void listenLocally(Stream<int> stream) { final subscription = stream.listen((_) {});void typedLocal(Stream<int> stream) { StreamSubscription<int> subscription = stream.listen(print);void fromController(StreamController<int> controller) { var subscription = controller.stream.listen((_) {}); final run = () { final subscription = stream.listen((_) {});void aliasedDeclaration(Stream<int> stream) { final subscription = stream.listen((_) {});void reassigned(Stream<int> stream, StreamSubscription<int>? holder) { final subscription = stream.listen((_) {});void constructorArgument(Stream<int> stream) { final subscription = stream.listen((_) {});void functionArgument(Stream<int> stream, void Function(StreamSubscription<int>) callback) { final subscription = stream.listen((_) {});void assertedAlias(StreamSubscription<int>? source) { final subscription = source!;void shadowedClosure(Stream<int> stream) { final subscription = stream.listen((_) {});void localCascade(Stream<int> stream) { final subscription = stream.listen((_) {})..pause();class ThroughThisPause { StreamSubscription<int>? subscription;class LeakedField { StreamSubscription<int>? subscription;void genericConstructorArgument(Stream<int> stream) { final subscription = stream.listen((_) {}); factory FactorySubscriptionBox(Stream<T> stream) { final subscription = stream.listen((_) {});void dynamicCallback(Stream<int> stream, dynamic callback) { final subscription = stream.listen((_) {});void assertedCallback(Stream<int> stream, void Function(StreamSubscription<int>)? callback) { final subscription = stream.listen((_) {});void immediateCallback(Stream<int> stream) { final subscription = stream.listen((_) {});void functionCallback(Stream<int> stream, Function callback) { final subscription = stream.listen((_) {}); void pass(Stream<int> stream) { final subscription = stream.listen((_) {});void getterCallback(Stream<int> stream, CallbackBox box) { final subscription = stream.listen((_) {});void chainedCascadeRelease() { final subscription = createSubscription();void indexedCallback(Stream<int> stream, List<void Function(StreamSubscription<int>)> callbacks) { final subscription = stream.listen((_) {});StreamSubscription<int> parenthesizedReturn(Stream<int> stream) { final subscription = stream.listen((_) {});void parenthesizedCancel(Stream<int> stream) { final subscription = stream.listen((_) {});void parenthesizedInitializer(StreamSubscription<int> source) { final subscription = (source); void start(Stream<int> stream) { final local = stream.listen((_) {});void otherReceiverTransfer(Stream<int> stream, ReleasedField receiver) { final subscription = stream.listen((_) {});class FieldCancelTearoff { late StreamSubscription<int> subscription;class SameNameIsNotSameField { StreamSubscription<int>? subscription;class ErasedSubscriptionField { SubscriptionView? subscription;void extensionSubscriptionLocal(Stream<int> stream) { SubscriptionInterface subscription = SubscriptionInterface(stream.listen((_) {}));Valid
import 'dart:async';
void consume(StreamSubscription<int> subscription) {}
class Watcher {
StreamSubscription<int>? _subscription;
void start(Stream<int> stream) {
_subscription = stream.listen((_) {});
}
Future<void> dispose() async {
await _subscription?.cancel();
}
}
class NullAsserted {
StreamSubscription<int>? _subscription;
void dispose() {
_subscription!.cancel();
}
}
class ThroughThis {
StreamSubscription<int>? subscription;
void dispose() {
this.subscription?.cancel();
}
}
class Paused {
StreamSubscription<int>? _pausedSubscription;
void start(Stream<int> stream) {
_pausedSubscription = stream.listen((_) {});
}
void hold() {
_pausedSubscription?.pause();
}
}
class Injected {
final StreamSubscription<int> subscription;
Injected(this.subscription);
}
class Initialized {
final StreamSubscription<int> subscription;
Initialized(Stream<int> stream) : subscription = stream.listen((_) {});
}
void cancelled(Stream<int> stream) {
final subscription = stream.listen((_) {});
subscription.cancel();
}
StreamSubscription<int> returned(Stream<int> stream) {
final subscription = stream.listen((_) {});
return subscription;
}
void passed(Stream<int> stream) {
final subscription = stream.listen((_) {});
consume(subscription);
}
void tornOff(Stream<int> stream, void Function(Future<void> Function()) register) {
final subscription = stream.listen((_) {});
register(subscription.cancel);
}
void cascaded(Stream<int> stream) {
final subscription = stream.listen((_) {});
subscription
..pause()
..cancel();
}
void unresolved(dynamic stream) {
final subscription = stream.listen((_) {});
subscription.pause();
}
void cancelledFromClosure(Stream<int> stream) {
final subscription = stream.listen((_) {});
final dispose = () => subscription.cancel();
dispose();
}
class CascadingField {
StreamSubscription<int>? subscription;
void hold() {
subscription?..pause();
}
}
abstract class ResourceBase {
StreamSubscription<int>? subscription;
}
class ResourceChild extends ResourceBase {
void dispose() => subscription?.cancel();
}
abstract class ThisResourceBase {
StreamSubscription<int>? subscription;
}
class ThisResourceChild extends ThisResourceBase {
void dispose() => this.subscription?.cancel();
}
void siblingCases(Stream<int> stream, int value) {
switch (value) {
case 1:
final subscription = stream.listen((_) {});
subscription.cancel();
case 2:
final subscription = stream.listen((_) {});
subscription.cancel();
}
}
void collectionScope(Stream<int> stream) {
final values = [for (var subscription = 0; subscription < 1; subscription++) subscription];
final subscription = stream.listen((_) {});
subscription.cancel();
}
void nestedFormal(Stream<int> stream, void callback(StreamSubscription<int> subscription)) {
final subscription = stream.listen((_) {});
subscription.cancel();
}Configure cancel-subscriptions
Set its severity in falcon.json. See the configuration guide.
{
"linter": {
"rules": {
"correctness": {
"cancel-subscriptions": "error"
}
}
}
}Or silence one occurrence with a suppression comment; the reason is required.
// falcon-ignore lint/correctness/cancel-subscriptions: <reason>More correctness rules
- check-is-not-closed-after-async-gaprecommendedbloc
Flags Bloc
addandemitcalls after an await without a freshisClosedcheck. - close-sinks
close_sinksrecommendedClose instances of
dart:coreSink. - collection-methods-unrelated-type
collection_methods_unrelated_typerecommendedReports collection lookups whose argument type is unrelated to the collection's type argument.
- comment-references
comment_referencesrecommendedFlags doc-comment references
[name]that resolve to nothing in scope. - correct-order-for-super-dispose
correct_order_for_super_disposerecommendedflutterRequire
super.dispose()to be the last call indispose. - depend-on-referenced-packages
depend_on_referenced_packagesrecommendedcross-fileRequire imported packages to be declared in
pubspec.yaml. - deprecated-member-use-from-same-package
deprecated_member_use_from_same_packageReports uses of deprecated declarations from their own package.
- diagnostic-describe-all-properties
diagnostic_describe_all_propertiesflutterRequires every public property of a
Diagnosticableclass to be referenced in its debug methods.