close-sinks
Same as close_sinks in Dart lints
- Group
- Correctness
- Path
lint/correctness/close-sinks- Default
- On (recommended)
- Origin
- Dart lints
- Rule
- 203 of 512
Close instances of dart:core Sink.
An unclosed sink can hold file handles, sockets, or buffered data and never signal completion to its listeners:
void log(File file) {
final sink = file.openWrite(); // BAD: never closed
sink.write('entry');
}
Future<void> log(File file) async {
final sink = file.openWrite();
sink.write('entry');
await sink.close(); // GOOD
}
Fields and local variables whose type provably implements Sink (released
by close) or dart:io Socket (released by close or destroy) 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; unresolved types stay silent.
Invalid
import 'dart:async';
import 'dart:io';
class Logger {
IOSink? _sink;
void open(File file) {
_sink = file.openWrite();
}
}
void writeLocally(File file) {
final sink = file.openWrite();
sink.write('entry');
}
void controllerLocal() {
final controller = StreamController<int>();
controller.add(1);
}
void typedLocal(File file) {
IOSink sink = file.openWrite();
sink.writeln('entry');
}
void socketLocal(Socket socket) {
Socket connection = socket;
Socket other = getSocket();
other.write('x');
connection.write('y');
}
Socket getSocket() => throw UnimplementedError();
class CascadingBloc {
final StreamController<int> cascadingEvents = StreamController<int>();
void addTwice(int event) {
this.cascadingEvents
..add(event)
..add(event);
}
}
class SinkBox {
final Sink<int> sink;
SinkBox(this.sink);
SinkBox.named(this.sink);
}
void constructorArgument() {
final sink = StreamController<int>();
SinkBox(sink);
}
void namedConstructorArgument() {
final sink = StreamController<int>();
SinkBox.named(sink);
}
void localCascade() {
final sink = StreamController<int>()..add(1);
}
void sameNamedBlocks() {
{
final sink = StreamController<int>();
sink.add(1);
}
{
final sink = StreamController<int>();
sink.close();
}
}
class LeakedField {
final sink = StreamController<int>();
}
class ReleasedField {
final sink = StreamController<int>();
void dispose() => sink.close();
}
class PassedField {
final sink = StreamController<int>();
void pass() => takeSink(sink);
}
void takeSink(Sink<int> value) {}
void functionArgument(void Function(Sink<int>) callback) {
final sink = StreamController<int>();
callback(sink);
}
class GenericSinkBox<T> {
GenericSinkBox.named(Sink<T> sink);
}
void genericConstructorArgument() {
final sink = StreamController<int>();
GenericSinkBox<int>.named(sink);
}
class FactorySinkBox<T> {
FactorySinkBox._(Sink<T> sink);
factory FactorySinkBox() {
final StreamController<T> sink = StreamController<T>.broadcast();
return FactorySinkBox<T>._(sink);
}
}
void dynamicCallback(dynamic callback) {
final sink = StreamController<int>();
callback(sink);
}
void functionCallback(Function callback) {
final sink = StreamController<int>();
callback(sink);
}
void assertedCallback(void Function(Sink<int>)? callback) {
final sink = StreamController<int>();
callback!(sink);
}
void immediateCallback() {
final sink = StreamController<int>();
((Sink<int> value) {})(sink);
}
class CallbackBox {
CallbackBox(this.callback);
final void Function(Sink<int>) callback;
void pass() {
final sink = StreamController<int>();
this.callback(sink);
}
}
void getterCallback(CallbackBox box) {
final sink = StreamController<int>();
box.callback(sink);
}
class ChainedSink implements Sink<int> {
void add(int value) {}
void close() {}
Sink<int> next() => this;
}
void chainedCascadeRelease() {
final sink = ChainedSink();
sink..next().close();
}
void indexedCallback(List<void Function(Sink<int>)> callbacks) {
final sink = StreamController<int>();
callbacks[0](sink);
}class Logger { IOSink? _sink;void writeLocally(File file) { final sink = file.openWrite();void controllerLocal() { final controller = StreamController<int>();void typedLocal(File file) { IOSink sink = file.openWrite(); Socket connection = socket; Socket other = getSocket();class CascadingBloc { final StreamController<int> cascadingEvents = StreamController<int>();void constructorArgument() { final sink = StreamController<int>();void namedConstructorArgument() { final sink = StreamController<int>();void localCascade() { final sink = StreamController<int>()..add(1); { final sink = StreamController<int>();class LeakedField { final sink = StreamController<int>();class PassedField { final sink = StreamController<int>();void functionArgument(void Function(Sink<int>) callback) { final sink = StreamController<int>();void genericConstructorArgument() { final sink = StreamController<int>(); factory FactorySinkBox() { final StreamController<T> sink = StreamController<T>.broadcast();void dynamicCallback(dynamic callback) { final sink = StreamController<int>();void functionCallback(Function callback) { final sink = StreamController<int>();void assertedCallback(void Function(Sink<int>)? callback) { final sink = StreamController<int>();void immediateCallback() { final sink = StreamController<int>(); void pass() { final sink = StreamController<int>();void getterCallback(CallbackBox box) { final sink = StreamController<int>();void chainedCascadeRelease() { final sink = ChainedSink();void indexedCallback(List<void Function(Sink<int>)> callbacks) { final sink = StreamController<int>();Valid
import 'dart:async';
import 'dart:io';
void consume(IOSink sink) {}
class Logger {
IOSink? _sink;
void open(File file) {
_sink = file.openWrite();
}
Future<void> dispose() async {
await _sink?.close();
}
}
class Pipe {
final StreamController<String> controller = StreamController<String>();
void dispose() => controller.close();
}
class SendingPipe {
final StreamController<String> sendingController = StreamController<String>();
void send(String value) => sendingController.add(value);
}
class Bloc {
final StreamController<int> _events = StreamController<int>();
void add(int event) => _events.add(event);
}
class NullableBloc {
StreamController<int>? nullableEvents;
void add(int event) => nullableEvents?.add(event);
}
class Injected {
final IOSink sink;
Injected(this.sink);
}
Future<void> closed(File file) async {
final sink = file.openWrite();
sink.write('entry');
await sink.close();
}
IOSink returned(File file) {
final sink = file.openWrite();
return sink;
}
void passed(File file) {
final sink = file.openWrite();
consume(sink);
}
void cascaded(File file) {
final sink = file.openWrite();
sink
..write('a')
..close();
}
void destroyed(Socket socket) {
final Socket connection = socketFor(socket);
connection.destroy();
}
Socket socketFor(Socket socket) => socket;
void unresolved(dynamic file) {
final sink = file.openWrite();
sink.write('entry');
}
void closedFromClosure() {
final sink = StreamController<int>();
final dispose = () => sink.close();
dispose();
}
void releaseLaterSection(File file) {
final sink = file.openWrite();
sink
..toString()
..close();
}
abstract class ResourceBase {
final events = StreamController<int>();
}
class ResourceChild extends ResourceBase {
void dispose() => events.close();
}
abstract class ThisResourceBase {
final events = StreamController<int>();
}
class ThisResourceChild extends ThisResourceBase {
void dispose() => this.events.close();
}
abstract class MixinResourceBase {
final events = StreamController<int>();
}
mixin ResourceReleaser on MixinResourceBase {
void dispose() => events.close();
}
abstract class ExtensionResourceBase {
final events = StreamController<int>();
}
extension ResourceRelease on ExtensionResourceBase {
void dispose() => events.close();
}
void siblingCases(int value) {
switch (value) {
case 1:
final sink = StreamController<int>();
sink.close();
case 2:
final sink = StreamController<int>();
sink.close();
}
}
void collectionScope() {
final values = [for (var sink = 0; sink < 1; sink++) sink];
final sink = StreamController<int>();
sink.close();
}
void mapCollectionScope() {
final values = {for (var sink = 0; sink < 1; sink++) sink: sink};
final sink = StreamController<int>();
sink.close();
}
void nestedFormal(File file, void callback(IOSink sink)) {
final sink = file.openWrite();
sink.close();
}
class ChainedReceiver {
ChainedReceiver next() => this;
void take(Sink<int> sink) {}
}
void chainedArgument(ChainedReceiver receiver) {
final sink = StreamController<int>();
receiver..next().take(sink);
}
final log = Object();
class MethodShadowsVariable {
void log(Sink<int> sink) {}
void write() {
final sink = StreamController<int>();
log(sink);
}
}
void localFunctionShadowsVariable() {
final take = Object();
{
void take(Sink<int> sink) {}
final sink = StreamController<int>();
take(sink);
}
}
abstract class ThisExtensionResourceBase {
final events = StreamController<int>();
}
extension ThisResourceRelease on ThisExtensionResourceBase {
void dispose() => this.events.close();
}
class LocalFunctionShadowsField {
LocalFunctionShadowsField(this.report);
final void Function(Object) report;
void run() {
void report(Sink<int> value) {}
final sink = StreamController<int>();
report(sink);
}
}Configure close-sinks
Set its severity in falcon.json. See the configuration guide.
{
"linter": {
"rules": {
"correctness": {
"close-sinks": "error"
}
}
}
}Or silence one occurrence with a suppression comment; the reason is required.
// falcon-ignore lint/correctness/close-sinks: <reason>More correctness rules
- 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. - discarded-futures
discarded_futuresDon't discard
Futures returned by calls in synchronous functions. - dispose-fieldsrecommendedflutter
Requires other owned disposable State fields to be released in
dispose().