unnecessary-null-checks
Same as unnecessary_null_checks in Dart lints
- Group
- Style
- Path
lint/style/unnecessary-null-checks- Default
- Off, opt in
- Origin
- Dart lints
- Rule
- 464 of 512
Flags a null assertion ! on a value that flows into a nullable context.
When x! is passed to a String? parameter, assigned to an int?
variable, or returned from a function returning Object?, the context
accepts null anyway: the ! only adds a runtime failure. The rule
fires on ! whose value directly is:
- the initializer of a variable or field declared with a nullable type;
- the right side of a plain
=assignment to a nullable target other than a local variable or parameter (the!may promote those), unless the asserted operand is the assigned name itself (x = x!); - an argument for a nullable parameter, except
Future.value(x!)andcompleter.complete(x!); - returned from a top-level or local function (including async functions
and top-level getters), or yielded to a nullable stream/iterable element,
including
yield*(matching the analyzer's element-type check); - an element of a list or set literal, or a key or value of a map literal, whose element types are nullable;
and on a null-assert pattern p! of a pattern assignment whose matched
value is nullable. In declarations, for loops, and case clauses the
! narrows the bound variables, so it is never flagged there.
dynamic and void accept null too. Unresolved contexts stay silent.
Index writes and right-hand operator operands use their instantiated
parameter types, not the enclosing expression's result context.
Grouping parentheses preserve the nullable context of the inner assertion.
Invalid
int? counter;
String? describe(int? value) => value?.toString();
void takeNullable(String? text) {}
int? lastLength;
class Holder {
Holder(this.name);
String? name;
String? copy;
String? _cache;
late final String? _late;
// Fields are never promotable on assignment, private or `late` alike.
void fill(String? value) {
_cache = value!;
_late = value!;
copy = value!;
}
}
int? doubled(int? x) => x!;
List<String>? first(List<String>? items) {
return items!;
}
void main(String? text, int? number, Holder holder, (int?, int) pair) {
takeNullable(text!);
int? copy = number!;
holder.copy = text!;
counter = number!;
final list = <int?>[number!];
final map = <String?, int?>{text!: 1};
int? a;
int b;
(a!, b) = pair;
print([copy, list, map, a, b, describe(1), doubled(2), first(null)]);
}
class UriContext {
String fromUri(Object? uri) => '$uri';
}
final UriContext uriContext = UriContext();
// A top-level variable receiver resolves to its declared type.
String fromUri(Object? uri) => uriContext.fromUri(uri!);
// Analyzer 3.13.5: adv-AdvA/repro/r14_unnecessary_null_checks.dart.
void takesDynamic(dynamic value) {}
class NullableOperators {
void operator []=(int index, int? value) {}
NullableOperators operator +(int? value) => this;
}
dynamic returnsDynamic(int? value) => value!;
void nullableContexts(int? value, NullableOperators receiver, Map<String, int?> map) {
takesDynamic(value!);
receiver[0] = value!;
receiver + value!;
map['key'] = value!;
}
Future<int?> nullableAsync(int? value) async => value!;
Stream<int?> nullableStream(int? value) async* {
yield value!;
}
// Official test_yieldSyncStar_iterableOfNullable and returnAsync_*_await.
Iterable<int?> nullableIterable(int? value) sync* {
yield value!;
}
Future<int?> nullableAwait(int? value) async => await value!;
int? get nullableGetter => getterValue!;
int? getterValue; void fill(String? value) { _cache = value!; _cache = value!; _late = value!; _late = value!; copy = value!; int? doubled(int? x) => x!;List<String>? first(List<String>? items) { return items!;void main(String? text, int? number, Holder holder, (int?, int) pair) { takeNullable(text!); takeNullable(text!); int? copy = number!; int? copy = number!; holder.copy = text!; holder.copy = text!; counter = number!; counter = number!; final list = <int?>[number!]; final list = <int?>[number!]; final map = <String?, int?>{text!: 1}; int b; (a!, b) = pair;// A top-level variable receiver resolves to its declared type.String fromUri(Object? uri) => uriContext.fromUri(uri!);}dynamic returnsDynamic(int? value) => value!;void nullableContexts(int? value, NullableOperators receiver, Map<String, int?> map) { takesDynamic(value!); takesDynamic(value!); receiver[0] = value!; receiver[0] = value!; receiver + value!; receiver + value!; map['key'] = value!;}Future<int?> nullableAsync(int? value) async => value!;Stream<int?> nullableStream(int? value) async* { yield value!;Iterable<int?> nullableIterable(int? value) sync* { yield value!;}Future<int?> nullableAwait(int? value) async => await value!;Future<int?> nullableAwait(int? value) async => await value!;int? get nullableGetter => getterValue!;Valid
import 'dart:async';
import 'dart:math';
void takeText(String text) {}
void takeNullable(String? text) {}
class Holder {
String? name;
int? size() => name!.length;
void fill(String? value) {
name = name!;
}
}
int count(int? value) => value!;
String? Function() closureOf(String? s) => () => s!;
String firstOr(List<String?> xs, bool Function(String?) p, String? fallback) =>
xs.firstWhere(p, orElse: () => fallback!)!;
void main(String? text, int? number, (int?, int) pair) async {
takeText(text!);
int copy = number!;
String? local;
local = text!;
final future = Future<int?>.value(number!);
final completer = Completer<int?>();
completer.complete(number!);
final list = [number!];
final (int a, int b) = (pair.$1!, pair.$2);
var (c!, d) = pair;
takeNullable(text);
print([copy, local, future, list, a, b, c, d, count(1)]);
}
// Analyzer 3.13.5: Future.value retains its null assertion even when its
// instantiated parameter is FutureOr<int?>.
void futureValue(int? value) => Future<int>.value(value!);
// intl number_format.dart:120,136: generic arguments are inferred from the
// non-nullable operands, not widened by the nullable assignment context.
int? significantDigits;
void setSignificantDigits(int value) {
significantDigits = min(significantDigits!, value);
significantDigits = max(significantDigits!, value);
}
// Flutter badge.dart/dropdown_menu.dart/icon.dart: the enclosing nullable
// context is not the right operand's expected parameter type for `??`.
void acceptsNullable(int? value) {}
int? fallbackValue;
void nullableFallback(int? value) {
acceptsNullable(value ?? fallbackValue!);
int? local = value ?? fallbackValue!;
significantDigits = value ?? fallbackValue!;
print(local);
}
class Registration<T extends Object> {
T? instance;
final ready = Completer<T>();
}
// Future.value and Completer.complete deliberately retain their null checks,
// including a generic receiver obtained from an instance field.
void completeRegistration<T extends Object>(Registration<T> registration) {
Future<T>.value(registration.instance!);
registration.ready.complete(registration.instance!);
}Configure unnecessary-null-checks
Set its severity in falcon.json. See the configuration guide.
{
"linter": {
"rules": {
"style": {
"unnecessary-null-checks": "error"
}
}
}
}Or silence one occurrence with a suppression comment; the reason is required.
// falcon-ignore lint/style/unnecessary-null-checks: <reason>More style rules
- unnecessary-nullable-for-final-variable-declarations
unnecessary_nullable_for_final_variable_declarationsrecommendedFlags a
finalorconstvariable given a nullable type but initialized to a provably non-null value. - unnecessary-parenthesis
unnecessary_parenthesisrecommendedFlags parentheses that can be removed without changing the meaning or hurting readability.
- unnecessary-raw-strings
unnecessary_raw_stringsrecommendedFlags raw string literals that contain no backslash or
$. - unnecessary-string-escapes
unnecessary_string_escapesrecommendedFlags backslashes that do not affect a string's value.
- unnecessary-string-interpolations
unnecessary_string_interpolationsrecommendedFlags a string literal whose entire content is a single interpolation of a provably non-nullable
String. - unnecessary-this
unnecessary_thisrecommendedFlags a redundant
this.qualifier on member access. - unreachable-from-main
unreachable_from_mainReport public declarations that no entry point of an executable library reaches.
- use-colored-box
use_colored_boxrecommendedflutterPrefers
ColoredBoxover aContainerthat only paints a color behind its child.