control-flow-in-finally
Same as control_flow_in_finally in Dart lints
- Group
- Suspicious
- Path
lint/suspicious/control-flow-in-finally- Default
- On (recommended)
- Origin
- Dart lints
- Rule
- 210 of 512
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
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;
}
}
} } finally { return 1; } finally { return; if (cond) { return; cleanup: { return; } finally { return; } finally { break; } finally { continue; } finally { if (cond) break;Valid
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.
{
"linter": {
"rules": {
"suspicious": {
"control-flow-in-finally": "error"
}
}
}
}Or silence one occurrence with a suppression comment; the reason is required.
// falcon-ignore lint/suspicious/control-flow-in-finally: <reason>More suspicious rules
- empty-catches
empty_catchesrecommendedFlags empty
catchblocks. - empty-statements
empty_statementsrecommendedFlags stray empty statements (a lone
;). - no-duplicate-case-values
no_duplicate_case_valuesrecommendedFlags semantically equal constant values within one switch.
- no-empty-block
no-empty-blockFlags empty blocks (
{}). - no-equal-arguments
no-equal-argumentsFlags an argument passed more than once in the same invocation.
- no-equal-then-else
no-equal-then-elserecommendedFlags an
if/elseor ternary whose two branches are identical. - no-magic-string
Extract inline string literals into named variables or constants.
- no-self-comparisons
no_self_comparisonsrecommendedFlags proven self-comparisons of side-effect-free scalar values.