Skip to content

Commit

Permalink
extend cases of unnecessary_null_checks/null_check_on_nullable_type_p…
Browse files Browse the repository at this point in the history
…arameter (dart-lang/linter#3258)

* extend cases of unnecessary_null_checks/null_check_on_nullable_type_parameter

* fix bad await handling

* add handling of async

* address review comments

* add test for typedef

* add some negative tests
  • Loading branch information
a14n authored Mar 15, 2022
1 parent 63cc085 commit e313403
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 1 deletion.
45 changes: 44 additions & 1 deletion lib/src/rules/unnecessary_null_checks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ DartType? getExpectedType(PostfixExpression node) {
var realNode =
node.thisOrAncestorMatching((e) => e.parent is! ParenthesizedExpression);
var parent = realNode?.parent;
var withAwait = parent is AwaitExpression;
if (withAwait) {
parent = parent!.parent;
}

// in return value
if (parent is ReturnStatement || parent is ExpressionFunctionBody) {
Expand All @@ -48,7 +52,32 @@ DartType? getExpectedType(PostfixExpression node) {
return null;
}
var staticType = parentExpression.staticType;
return staticType is FunctionType ? staticType.returnType : null;
if (staticType is! FunctionType) {
return null;
}
staticType = staticType.returnType;
if (withAwait || parentExpression.body.keyword?.lexeme == 'async') {
return staticType.isDartAsyncFuture || staticType.isDartAsyncFutureOr
? (staticType as ParameterizedType?)?.typeArguments.first
: null;
} else {
return staticType;
}
}
// in yield value
if (parent is YieldStatement) {
var parentExpression = parent.thisOrAncestorOfType<FunctionExpression>();
if (parentExpression == null) {
return null;
}
var staticType = parentExpression.staticType;
if (staticType is! FunctionType) {
return null;
}
staticType = staticType.returnType;
return staticType.isDartCoreIterable || staticType.isDartAsyncStream
? (staticType as ParameterizedType).typeArguments.first
: null;
}
// assignment
if (parent is AssignmentExpression &&
Expand All @@ -71,6 +100,20 @@ DartType? getExpectedType(PostfixExpression node) {
}
return parentElement.parameters.first.type;
}
// as member of list
if (parent is ListLiteral) {
return (parent.staticType as ParameterizedType?)?.typeArguments.first;
}
// as member of set
if (parent is SetOrMapLiteral && parent.isSet) {
return (parent.staticType as ParameterizedType?)?.typeArguments.first;
}
// as member of map
if (parent is MapLiteralEntry) {
var typeParameters =
(parent.parent! as SetOrMapLiteral).staticType as ParameterizedType?;
return typeParameters?.typeArguments[parent.key == node ? 0 : 1];
}
// as parameter of function
if (parent is NamedExpression) {
realNode = parent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ T m30<T>(T? p) {
t = p!; // LINT
return t;
}
Future<T> m40<T extends Object?>(T? p) async => await p!; // LINT
Future<List<T>> m41<T extends Object?>(T? p) async => await [p!]; // LINT
List<T> m50<T>(T? p) => [p!]; // LINT
Set<T> m60<T>(T? p) => {p!}; // LINT
Map<String, T> m71<T>(T? p) => {'': p!}; // LINT
Map<T, String> m72<T>(T? p) => {p!: ''}; // LINT
Iterable<T> m80<T>(T? p) sync* {yield p!;} // LINT
Stream<T> m90<T>(T? p) async* {yield p!;} // LINT
class C<T> {
late T t;
m(T? p) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,30 @@ f5(int? p) {
v1 ??= p!; // OK
}

Future<int?> f6(int? p) async => await p!; // LINT
Future<int> f6ok(int? p) async => await p!; // OK
List<int?> f7(int? p) => [p!]; // LINT
List<int> f7ok(int? p) => [p!]; // OK
Set<int?> f8(int? p) => {p!}; // LINT
Set<int> f8ok(int? p) => {p!}; // OK
Map<int?, String> f9(int? p) => {p!: ''}; // LINT
Map<int, String> f9ok(int? p) => {p!: ''}; // OK
Map<String, int?> f10(int? p) => {'': p!}; // LINT
Map<String, int> f10ok(int? p) => {'': p!}; // OK
Iterable<int?> f11(int? p) sync* {yield p!;} // LINT
Iterable<int> f11ok(int? p) sync* {yield p!;} // OK
Stream<int?> f12(int? p) async* {yield p!;} // LINT
Stream<int> f12ok(int? p) async* {yield p!;} // OK
Future<void> f13(int? p) async {
var f = Future(() => p);
int? i;
i = await f!; // LINT
}
Future<int?> f14(int? p) async => p!; // LINT
Future<int> f14ok(int? p) async => p!; // OK
dynamic f15(int? p) async => p!; // OK

typedef F16 = Future<int?>;
F16 f16(int? p) async => p!; // LINT
typedef F16ok = Future<int>;
F16ok f16ok(int? p) async => p!; // OK

0 comments on commit e313403

Please sign in to comment.