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.
Why use this rule
Section titled “Why use this rule”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
A lookup whose real return was removed in a refactor and never put back:
class SessionStore { final Map<String, String> _tokens = {};
String? tokenFor(String userId) { if (userId.isEmpty) return null; if (!_tokens.containsKey(userId)) return null; return null; // the read was lost; every caller null-checks for nothing }}Return the value the signature promises:
class SessionStore { final Map<String, String> _tokens = {};
String? tokenFor(String userId) { if (userId.isEmpty) return null; return _tokens[userId]; }}Or, if the function genuinely produces nothing, say so in the type:
class SessionStore { final Map<String, String> _tokens = {};
void forget(String userId) { _tokens.remove(userId); }}A bare return; counts as null
Section titled “A bare return; counts as null”In a nullable-returning function return; yields null, so this reports too — the mix of styles is often what hid the problem:
class Draft { String? _title;
String? titleOrNull() { if (_title == null) return; return null; }}Known limitations
Section titled “Known limitations”@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. Their declared type wraps the value — Future<String?> — so “every return is null” does not carry the same meaning, and a Future<String?> returning null is a normal “not found”.
The return type must be written out. An omitted annotation is inferred as Null, which the analyzer surfaces on its own terms, so this rule only reads types the author typed.
A function with no return at all is not reported. The analyzer already covers that with body_might_complete_normally_nullable.
Returns inside a nested closure belong to that closure, not to the enclosing function.
Configuration
Section titled “Configuration”This rule is in the recommended preset, so it is on with
preset: recommended or preset: opinionated.
To turn it off:
rules: function_always_returns_null: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”function_always_returns_same_value— Flag a function whose every return yields the same constant.avoid_non_null_assertion— Don’t assert away null with the ! operator.avoid_accessing_other_classes_private_members— Make the underscore mean what everyone reads it as.avoid_commented_out_code— Detect and flag commented-out code.