control-flow-in-finally

Same as control_flow_in_finally in Dart lints

Path
lint/suspicious/control-flow-in-finally
Default
On (recommended)
Origin
Dart lints
Rule
210 of 512
recommended

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

Control flow leaving a finally overrides whatever the try or catch was doing, including silently discarding an exception that was still propagating — the error simply vanishes — and masking any value the try was about to return. Keep finally blocks limited to cleanup and let exceptions and returns flow through. A break or continue targeting a loop or switch declared inside the finally is fine and left alone, as are closures defined within it; only flow that escapes the finally itself is reported.

Invalid

example.dartdart
int f1() {
  try {
    doThing();
  } finally {
    return 1;
  }
}

void f2() {
  try {
    doThing();
  } finally {
    return;
  }
}

void f3(bool cond) {
  try {
    doThing();
  } finally {
    if (cond) {
      return;
    }
  }
}

// A label on a block inside finally must not hide the escaping return.
void labeledInFinally() {
  try {
    doThing();
  } finally {
    cleanup: {
      return;
    }
  }
}

// A label on the whole try must not hide its finally block.
void labeledTry() {
  outer:
  try {
    doThing();
  } finally {
    return;
  }
}

void f4() {
  for (var i = 0; i < 10; i++) {
    try {
      doThing();
    } finally {
      break;
    }
  }
}

void f5() {
  for (var i = 0; i < 10; i++) {
    try {
      doThing();
    } finally {
      continue;
    }
  }
}

void f6(bool cond) {
  for (var i = 0; i < 10; i++) {
    try {
      doThing();
    } finally {
      if (cond) break;
    }
  }
}
Avoid control flow (return/break/continue) that escapes a finally block.
4 } finally {
5 return 1;
Avoid control flow (return/break/continue) that escapes a finally block.
12 } finally {
13 return;
Avoid control flow (return/break/continue) that escapes a finally block.
21 if (cond) {
22 return;
Avoid control flow (return/break/continue) that escapes a finally block.
32 cleanup: {
33 return;
Avoid control flow (return/break/continue) that escapes a finally block.
43 } finally {
44 return;
Avoid control flow (return/break/continue) that escapes a finally block.
52 } finally {
53 break;
Avoid control flow (return/break/continue) that escapes a finally block.
62 } finally {
63 continue;
Avoid control flow (return/break/continue) that escapes a finally block.
72 } finally {
73 if (cond) break;

Valid

example.dartdart
void g1() {
  try {
    doThing();
  } finally {
    for (var i = 0; i < 10; i++) {
      if (i > 5) break;
    }
  }
}

void g2(int x) {
  try {
    doThing();
  } finally {
    switch (x) {
      case 1:
        break;
      default:
        break;
    }
  }
}

void g3() {
  try {
    doThing();
  } finally {
    final f = () {
      return;
    };
    f();
  }
}

void g4() {
  try {
    doThing();
  } finally {
    doCleanup();
  }
}

int g5() {
  try {
    return 1;
  } finally {
    doCleanup();
  }
}

void g6(bool cond) {
  try {
    doThing();
  } finally {
    while (cond) {
      continue;
    }
  }
}

// A labeled loop declared inside finally is a valid break target, so a
// labeled break that stays within the finally does not escape it.
void g7() {
  try {
    doThing();
  } finally {
    outer:
    for (var i = 0; i < 3; i++) {
      break outer;
    }
  }
}

Configure control-flow-in-finally

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

falcon.jsonjson
{
  "linter": {
    "rules": {
      "suspicious": {
        "control-flow-in-finally": "error"
      }
    }
  }
}

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

example.dartdart
// falcon-ignore lint/suspicious/control-flow-in-finally: <reason>
  • 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.

  • no-self-comparisonsno_self_comparisonsrecommended

    Flags proven self-comparisons of side-effect-free scalar values.