cascade-after-null-coalescing

Path
lint/suspicious/cascade-after-null-coalescing
Default
Off, opt in
Origin
Falcon
Rule
198 of 512

Flags a cascade after an unparenthesized null-coalescing expression.

In a ?? b..method(), the cascade applies to the result of a ?? b, not just b. Parenthesize the receiver as (a ?? b)..method() or the fallback as a ?? (b..method()) to make that choice explicit. The receiver may also be a ternary whose trailing else expression ends in ??. Explicit receiver parentheses and parentheses around the fallback are respected, and ?? nested in other receiver subexpressions is not flagged. Both ordinary and null-aware cascades are checked without type inference.

Invalid

example.dartdart
class Box {
  int value = 0;
  void reset() { value = 0; }
}

Box choose(Box? first, Box second) => first ?? second..reset();

void cascades(Box? first, Box? middle, Box second) {
  final assigned = first ?? second..value = 1;
  first ?? second..reset()..value = 2;
  final nested = first ?? middle ?? second..reset();
  final list = [first ?? second..reset()];
  final wrapped = (first ?? second..reset());
  final nullable = first ?? middle?..reset();
  final operandOnly = first ?? (second)..reset();
  final ternary = first == null ? second : middle ?? second..reset();
  final deepTernary = first == null ? second : middle == null ? second : first ?? second..reset();
}
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
5
6Box choose(Box? first, Box second) => first ?? second..reset();
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
8void cascades(Box? first, Box? middle, Box second) {
9 final assigned = first ?? second..value = 1;
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
9 final assigned = first ?? second..value = 1;
10 first ?? second..reset()..value = 2;
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
10 first ?? second..reset()..value = 2;
11 final nested = first ?? middle ?? second..reset();
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
11 final nested = first ?? middle ?? second..reset();
12 final list = [first ?? second..reset()];
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
12 final list = [first ?? second..reset()];
13 final wrapped = (first ?? second..reset());
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
13 final wrapped = (first ?? second..reset());
14 final nullable = first ?? middle?..reset();
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
14 final nullable = first ?? middle?..reset();
15 final operandOnly = first ?? (second)..reset();
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
15 final operandOnly = first ?? (second)..reset();
16 final ternary = first == null ? second : middle ?? second..reset();
Cascade after `??` applies to the whole receiver; parenthesize the receiver or fallback.
16 final ternary = first == null ? second : middle ?? second..reset();
17 final deepTernary = first == null ? second : middle == null ? second : first ?? second..reset();

Valid

example.dartdart
class Box {
  int value = 0;
  void reset() { value = 0; }
}

Box choose(Box? first, Box second) => (first ?? second)..reset();

void cascades(Box? first, Box? middle, Box second, bool condition) {
  final explicitReceiver = (first ?? second)..value = 1;
  final explicitFallback = first ?? (second..reset());
  final multiple = (first ?? second)..reset()..value = 2;
  final nestedReceiver = ((first ?? middle) ?? second)..reset();
  final nestedFallback = first ?? (middle ?? (second..reset()));
  final ordinary = second..reset();
  final nullAware = (first ?? middle)?..reset();
  final conditional = (condition ? first : second)?..reset();
  final methodCall = (first ?? second).reset();
  final ternaryReceiver = (condition ? first : middle ?? second)..reset();
  final ternaryFallback = condition ? second : (middle ?? (second..reset()));
  final parenthesizedElse = condition ? second : (first ?? second)..reset();
  final coalescingThen = condition ? first ?? second : second..reset();
}

Configure cascade-after-null-coalescing

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

falcon.jsonjson
{
  "linter": {
    "rules": {
      "suspicious": {
        "cascade-after-null-coalescing": "error"
      }
    }
  }
}

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

example.dartdart
// falcon-ignore lint/suspicious/cascade-after-null-coalescing: <reason>
  • control-flow-in-finallycontrol_flow_in_finallyrecommended

    Flags return, break, or continue that escapes a finally block.

  • empty-catchesempty_catchesrecommended

    Flags empty catch blocks.

  • empty-statementsempty_statementsrecommended

    Flags stray empty statements (a lone ;).

  • no-duplicate-case-valuesno_duplicate_case_valuesrecommended

    Flags semantically equal constant values within one switch.

  • no-empty-blockno-empty-block

    Flags empty blocks ({}).

  • no-equal-argumentsno-equal-arguments

    Flags an argument passed more than once in the same invocation.

  • no-equal-then-elseno-equal-then-elserecommended

    Flags an if/else or ternary whose two branches are identical.

  • no-magic-string

    Extract inline string literals into named variables or constants.