Skip to content

function_always_returns_null

v1.0.0 Warning Code Quality

This rule flags a function declared with a nullable return type whose every return yields null. No caller can ever receive a value, yet every caller is forced to null-check.

A function typed String? promises that a String is sometimes possible. When every path returns null, that promise is false: the nullable type is pure overhead, spreading null checks through the callers for a value that never arrives.

In practice this is almost always a leftover — an unfinished implementation, or a refactor that removed the real return and left the signature behind. Either way the declaration and the body disagree, and the body is the one telling the truth.

See also: Dart: understanding null safety

String? lookup(String key) {
if (key.isEmpty) return null;
return null; // nothing can ever come back
}

Return the value the signature promises:

String? lookup(String key) {
if (key.isEmpty) return null;
return _cache[key];
}

Or, if the function genuinely produces nothing, say so in the type:

void record(String key) {
_log.add(key);
}

@override methods are skipped: an override must keep the inherited signature, so the author may have no freedom to change it.

async and generator bodies are skipped too. Their declared type wraps the value — Future<String?> — so “every return is null” does not carry the same meaning.

A function with no return at all is not this rule’s concern; the analyzer already reports body_might_complete_normally_nullable for it. Nor is a bare return;, which the analyzer flags as return_without_value. Returns inside a nested closure are attributed to that closure, not the enclosing function.

This rule is in the recommended preset, so it is on with preset: recommended or preset: opinionated.

To turn it off:

many_lints.yaml
rules:
function_always_returns_null: false

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