avoid-returning-this

Same as avoid_returning_this in Dart lints

Group
Style
Path
lint/style/avoid-returning-this
Default
Off, opt in
Origin
Dart lints
Rule
132 of 512

Flags methods that return this only to enable a fluent interface.

Dart's cascade operator (..) chains calls on the same receiver without any cooperation from the API, so a method returning its own receiver adds nothing but a misleading return type. Return void and let callers cascade.

class Builder {
  Builder add(int x) {        // flagged at `return this`
    _items.add(x);
    return this;
  }
  Builder clear() => this;    // flagged at `clear`
}

Only methods whose declared return type is the enclosing class, enum, or mixin and whose every return yields an unparenthesized this are reported. Methods that override an inherited member are exempt.

Invalid

example.dartdart
class Builder {
  final List<int> _items = [];

  Builder add(int x) {
    _items.add(x);
    return this;
  }

  Builder clear() => this;

  Builder addAll(List<int> xs) {
    if (xs.isEmpty) {
      return this;
    }
    _items.addAll(xs);
    return this;
  }

  Builder get self => this;
}

class Generic<T> {
  Generic<T> touch() => this;
}

mixin Fluent {
  Fluent reset() {
    return this;
  }
}

enum Level {
  low;

  Level same() => this;
}

// An unresolved superclass does not establish an override.
class Remote extends SomeUnknownBase {
  Remote ping() => this;
}

class NestedReturns {
  NestedReturns run() {
    NestedReturns local() {
      return NestedReturns();
    }
    final callback = () {
      return NestedReturns();
    };
    return this;
  }
}

class ControlReturns {
  ControlReturns guarded(bool fail) {
    try {
      if (fail) throw StateError('failed');
      return this;
    } catch (_) {
      return this;
    } finally {
      return this;
    }
  }

  ControlReturns branches(int value) {
    switch (value) {
      case 0:
        return this;
      default:
        outer:
        for (var i = 0; i < value; i++) {
          if (i == 1) return this;
          break outer;
        }
    }
    while (value-- > 0) {
      return this;
    }
    do {
      return this;
    } while (false);
  }

  ControlReturns callbacks() {
    final callback = () {
      return this;
    };
    ControlReturns local() {
      return this;
    }
    return this;
  }
}
Don't return `this` from a method.
5 _items.add(x);
6 return this;
Don't return `this` from a method.
8
9 Builder clear() => this;
Don't return `this` from a method.
12 if (xs.isEmpty) {
13 return this;
Don't return `this` from a method.
18
19 Builder get self => this;
Don't return `this` from a method.
22class Generic<T> {
23 Generic<T> touch() => this;
Don't return `this` from a method.
27 Fluent reset() {
28 return this;
Don't return `this` from a method.
34
35 Level same() => this;
Don't return `this` from a method.
39class Remote extends SomeUnknownBase {
40 Remote ping() => this;
Don't return `this` from a method.
50 };
51 return this;
Don't return `this` from a method.
58 if (fail) throw StateError('failed');
59 return this;
Don't return `this` from a method.
69 case 0:
70 return this;
Don't return `this` from a method.
92 }
93 return this;

Valid

example.dartdart
class Builder {
  final List<int> _items = [];

  // Use cascades instead of returning `this`.
  void add(int x) => _items.add(x);

  // Returns a new instance, not `this`.
  Builder copy() => Builder().._items.addAll(_items);

  // Some paths return something else.
  Builder pick(Builder other, bool first) {
    if (first) return this;
    return other;
  }

  // Return type is not the enclosing type.
  Object identity() => this;

  // Operators are not methods.
  Builder operator +(int x) => this;
}

// Overriding an inherited member is required by the interface.
abstract class Chain {
  Chain next();
}

class Link implements Chain {
  @override
  Chain next() => this;
}

class Node extends Link {
  @override
  Link next() => this;
}


// Extensions are not checked.
extension on String {
  String same() => this;
}

// Static methods cannot return `this`; nested closures do not count.
class Holder {
  Holder run() {
    final f = () => this;
    return Holder();
  }
}

class GroupedReceiver {
  GroupedReceiver arrow() => (this);
  GroupedReceiver block() {
    return (/* grouping is significant to this lint */ this);
  }
  GroupedReceiver mixed(bool flag) {
    if (flag) return this;
    return ((this));
  }
}

class NestedOnly {
  NestedOnly run() {
    NestedOnly local() {
      return this;
    }
    final callback = () {
      return this;
    };
  }
}

class ReturnedClosure {
  ReturnedClosure run() {
    return (() {
      return this;
    })();
  }
}

Configure avoid-returning-this

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "style": {
        "avoid-returning-this": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/style/avoid-returning-this: <reason>