Skip to content

avoid_deep_nesting

v1.0.0WarningConfigurableCode Quality

This rule flags control flow nested more deeply than the configured budget. Each level of if, for, while, do, try or switch counts.

Each level is another condition the reader has to hold to know why a line runs at all. Depth is also more actionable than a complexity score: an early return, a guard clause or an extracted method removes a whole level, where a complexity count only says something is wrong.

This rule is in the pedantic preset. The default budget is max_depth: 4, and one diagnostic is reported per function, anchored at the statement that first crosses the limit.

Five levels. Nothing on the handle(cell) line says why it runs:

void process(List<List<int>> rows, bool enabled) {
if (enabled) {
for (final row in rows) {
for (final cell in row) {
if (cell > 0) {
while (cell > 1) { // reported here — level 5
handle(cell);
}
}
}
}
}
}
void handle(int cell) {}

Invert the outer guard, continue past the filter, and lift the inner loop into its own function. Same behaviour, two levels each:

void process(List<List<int>> rows, bool enabled) {
if (!enabled) return;
for (final row in rows) {
_processRow(row);
}
}
void _processRow(List<int> row) {
for (final cell in row) {
if (cell <= 0) continue;
_handleRepeatedly(cell);
}
}
void _handleRepeatedly(int cell) {
while (cell > 1) {
handle(cell);
}
}
void handle(int cell) {}

A dispatch chain reads flat and is counted flat, however long it grows:

// Not reported — this is one level, not five.
String label(int code) {
if (code < 100) {
return 'informational';
} else if (code < 200) {
return 'success';
} else if (code < 300) {
return 'redirect';
} else if (code < 400) {
return 'client error';
} else if (code < 500) {
return 'server error';
} else {
return 'unknown';
}
}

A callback’s body is not reached through the enclosing nest, so it is measured against the budget on its own:

// Not reported — the outer function has one level, the callback three.
void install(List<int> rows) {
rows.forEach((row) {
if (row > 0) {
for (var i = 0; i < row; i++) {
if (i.isEven) {
print(i);
}
}
}
});
}

Four is a guard, a loop, a branch and the work. Drop it to three if you want the work itself at the shallowest level:

many_lints.yaml
rules:
avoid_deep_nesting:
max_depth: 3

Only block bodies are examined. A function written with an expression body (=>) is skipped entirely, however much nesting a conditional expression hides in it.

else if never adds a level, but an else { if (...) } written with braces does.

This rule is in the pedantic preset, so it is enabled by preset: pedantic or by name:

many_lints.yaml
rules:
avoid_deep_nesting:
enabled: true
analysis_options.yaml
many_lints:
rules:
avoid_deep_nesting:
max_depth: 4
Option Type Default Description
max_depth int 4 How many levels of control flow may nest

To disable this rule:

many_lints.yaml
rules:
avoid_deep_nesting: false

To keep the rule on but skip certain paths, use per-rule exclude.