unnecessary-nullable-return-type

Same as unnecessary_nullable_return_type in Pyramid Lint

Path
lint/correctness/unnecessary-nullable-return-type
Default
Off, opt in
Origin
Pyramid Lint
Rule
468 of 512

Disallow nullable return types on functions that never return null.

Flags a function or method whose return type is nullable (T?) even though every return in its body yields a provably non-null value. A needless ? forces callers to null-check a result that can never be null, spreading defensive code that adds no safety. Drop the ? from the return type. Only the outer nullability counts, so Future<T?> and List<int?> are left alone. Without full type resolution the check is conservative.

Invalid

example.dartdart
// Bad: an outer-nullable return type whose every return is a non-null literal.

String? getStatus() {
  return 'active';
}

int? getNumber() {
  return 42;
}

// The outer `?` (after `List<...>`) counts; the returned list is non-null.
List<String?>? getItems() {
  return ['a', 'b', 'c'];
}

bool? isValid() {
  if (DateTime.now().isUtc) {
    return true;
  }
  return false;
}

class Holder {
  String? get label => 'x';
}
Function return type is unnecessarily nullable.
2
3String? getStatus() {
Function return type is unnecessarily nullable.
6
7int? getNumber() {
Function return type is unnecessarily nullable.
11// The outer `?` (after `List<...>`) counts; the returned list is non-null.
12List<String?>? getItems() {
Function return type is unnecessarily nullable.
15
16bool? isValid() {
Function return type is unnecessarily nullable.
23class Holder {
24 String? get label => 'x';

Valid

example.dartdart
// Good: return type is non-nullable when function never returns null

Future<String> getName() async {
  return 'hello';
}

String getStatus() {
  return 'active';
}

int getNumber() {
  return 42;
}

List<String> getItems() {
  return ['a', 'b', 'c'];
}

Future<bool> isValid() async {
  return true;
}

// Good: nullable return type when function can return null

String? maybeGetName(bool shouldReturn) {
  if (shouldReturn) {
    return 'hello';
  }
  return null;
}

int? findIndex(List<int> items, int target) {
  try {
    return items.indexOf(target);
  } catch (e) {
    return null;
  }
}

// Good: only the OUTER `?` counts — a nullable type argument does not.
Future<String?> asyncName() async {
  return 'hello';
}

Future<bool?> asyncValid() async {
  return true;
}

// Good: returns we cannot prove non-null are not flagged.
String? fromVariable(String value) {
  return value;
}

String? fromCall() {
  return compute();
}

String? withNullBranch(bool flag) {
  if (flag) {
    return 'x';
  }
  return null;
}

String? onlyDelayedReturns() {
  String? local() {
    return 'local';
  }
  final callback = () {
    return 'callback';
  };
}

String? nullableOuterAfterNonNullClosure() {
  final callback = () {
    return 'callback';
  };
  return null;
}

Configure unnecessary-nullable-return-type

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

falcon.jsonjson
{
  "linter": {
    "rules": {
      "correctness": {
        "unnecessary-nullable-return-type": "error"
      }
    }
  }
}

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

example.dartdart
// falcon-ignore lint/correctness/unnecessary-nullable-return-type: <reason>
  • unnecessary-statementsunnecessary_statementsrecommended

    Flags expression statements whose expression has no clear effect.

  • unrelated-type-equality-checksunrelated_type_equality_checksrecommended

    Reports ==/!= comparisons and relational ==/!= patterns whose operand types are unrelated.

  • unused-codecheck-unused-codecross-file

    Report public top-level declarations that nothing references.

  • unused-dependenciescross-file

    Report regular pubspec dependencies with no Dart import or export in the analyzed package.

  • unused-filescheck-unused-filescross-file

    Report lib/ files that no project entrypoint can reach.

  • use-build-context-synchronouslyuse_build_context_synchronouslyrecommendedflutter

    Flags BuildContext use after an asynchronous gap without a mounted guard.

  • use-closest-build-contextrecommendedflutter

    Use the nearest BuildContext parameter inside nested callbacks.

  • use-key-in-widget-constructorsuse_key_in_widget_constructorsrecommendedflutter

    Require public widget constructors to accept or initialize a Flutter Key.