Add null checks for spread operator
Change-Id: Ib3b0f46643a24e22e0344a4505743518f885e9e5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102501
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
diff --git a/pkg/analyzer/lib/src/error/literal_element_verifier.dart b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
index ba32987..1e9916c 100644
--- a/pkg/analyzer/lib/src/error/literal_element_verifier.dart
+++ b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
@@ -146,11 +146,12 @@
}
InterfaceType iterableType;
- var iterableObjectType = typeProvider.iterableObjectType;
+ var iterableDynamicType = (typeProvider.iterableDynamicType as TypeImpl)
+ .withNullability(NullabilitySuffix.question);
if (expressionType is InterfaceTypeImpl &&
- typeSystem.isSubtypeOf(expressionType, iterableObjectType)) {
+ typeSystem.isSubtypeOf(expressionType, iterableDynamicType)) {
iterableType = expressionType.asInstanceOf(
- iterableObjectType.element,
+ iterableDynamicType.element,
);
}
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 93bc24d..f860e69 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1293,6 +1293,14 @@
}
@override
+ void visitSpreadElement(SpreadElement node) {
+ if (node.spreadOperator.type != TokenType.PERIOD_PERIOD_PERIOD_QUESTION) {
+ _checkForNullableDereference(node.expression);
+ }
+ super.visitSpreadElement(node);
+ }
+
+ @override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
DartType type =
resolutionMap.staticElementForConstructorReference(node)?.type;
diff --git a/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart b/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
index 6dc6cfc..a78ae34 100644
--- a/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unchecked_use_of_nullable_value_test.dart
@@ -626,6 +626,33 @@
]);
}
+ test_spread_nonNullable() async {
+ await assertNoErrorsInCode(r'''
+m() {
+ var list = [];
+ [...list];
+}
+''');
+ }
+
+ test_spread_nullable() async {
+ await assertErrorCodesInCode(r'''
+m() {
+ List? list;
+ [...list];
+}
+''', [StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE]);
+ }
+
+ test_spread_nullable_question() async {
+ await assertNoErrorsInCode(r'''
+m() {
+ List? list;
+ [...?list];
+}
+''');
+ }
+
test_ternary_condition_nullable() async {
await assertErrorsInCode(r'''
m() {
diff --git a/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart b/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart
index a73735c..56679cf 100644
--- a/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart
+++ b/tests/language_2/nnbd/static_errors/unchecked_use_of_nullable_test.dart
@@ -53,11 +53,13 @@
for(var i in list) {}; //# 36: compile-time error
await for(var i in stream) {}; //# 37: compile-time error
assert(cond); //# 38: compile-time error
+ [...list]; //# 39: compile-time error
+ [...?list]; //# 40: ok
}
generator() sync* {
Iterable? iter;
- yield* iter; //# 39: compile-time error
+ yield* iter; //# 41: compile-time error
}
void typeParametersNullableBounds<IQ extends int?, BQ extends bool?, LQ extends List?, FQ extends Function?, SQ extends Stream?>(
@@ -68,47 +70,47 @@
List<FQ> funcList,
SQ stream,
) async {
- x.isEven; //# 40: compile-time error
- x.round(); //# 41: compile-time error
- x.toString(); //# 42: ok
- x.hashCode; //# 43: ok
- x.runtimeType; //# 44: ok
- x.noSuchMethod(Invocation.method(#toString, [])); //# 45: ok
- x + 1; //# 46: compile-time error
- -x; //# 47: compile-time error
- x++; //# 48: compile-time error
- ++x; //# 49: compile-time error
- x..isEven; //# 50: compile-time error
- list[0]; //# 51: compile-time error
- list[0] = 0; //# 52: compile-time error
- x += 1; //# 53: compile-time error
- x ??= x; //# 54: ok
- x.round; //# 55: compile-time error
- x.toString; //# 56: ok
- x.noSuchMethod; //# 57: ok
- func(); //# 58: compile-time error
- funcList[0](); //# 59: compile-time error
- funcList.single(); //# 60: compile-time error
- throw x; //# 61: compile-time error
- cond || true; //# 62: compile-time error
- true || cond; //# 63: compile-time error
- cond && true; //# 64: compile-time error
- true && cond; //# 65: compile-time error
- !cond; //# 66: compile-time error
- cond ? null : null; //# 67: compile-time error
- if (cond) {} //# 68: compile-time error
- while (cond) {} //# 69: compile-time error
- for (;cond;){} //# 70: compile-time error
- do {} while (cond); //# 71: compile-time error
- cond!; //# 72: ok
- cond ?? null; //# 73: ok
- cond == null; //# 74: ok
- cond != null; //# 75: ok
- x?.isEven; //# 76: ok
- x?.round(); //# 77: ok
- for(var i in list) {}; //# 78: compile-time error
- await for(var i in stream) {}; //# 79: compile-time error
- assert(cond); //# 39: compile-time error
+ x.isEven; //# 42: compile-time error
+ x.round(); //# 43: compile-time error
+ x.toString(); //# 44: ok
+ x.hashCode; //# 45: ok
+ x.runtimeType; //# 46: ok
+ x.noSuchMethod(Invocation.method(#toString, [])); //# 47: ok
+ x + 1; //# 48: compile-time error
+ -x; //# 49: compile-time error
+ x++; //# 50: compile-time error
+ ++x; //# 51: compile-time error
+ x..isEven; //# 52: compile-time error
+ list[0]; //# 53: compile-time error
+ list[0] = 0; //# 54: compile-time error
+ x += 1; //# 55: compile-time error
+ x ??= x; //# 56: ok
+ x.round; //# 57: compile-time error
+ x.toString; //# 58: ok
+ x.noSuchMethod; //# 59: ok
+ func(); //# 60: compile-time error
+ funcList[0](); //# 61: compile-time error
+ funcList.single(); //# 62: compile-time error
+ throw x; //# 63: compile-time error
+ cond || true; //# 64: compile-time error
+ true || cond; //# 65: compile-time error
+ cond && true; //# 66: compile-time error
+ true && cond; //# 67: compile-time error
+ !cond; //# 68: compile-time error
+ cond ? null : null; //# 69: compile-time error
+ if (cond) {} //# 70: compile-time error
+ while (cond) {} //# 71: compile-time error
+ for (;cond;){} //# 72: compile-time error
+ do {} while (cond); //# 73: compile-time error
+ cond!; //# 74: ok
+ cond ?? null; //# 75: ok
+ cond == null; //# 76: ok
+ cond != null; //# 77: ok
+ x?.isEven; //# 78: ok
+ x?.round(); //# 79: ok
+ for(var i in list) {}; //# 80: compile-time error
+ await for(var i in stream) {}; //# 81: compile-time error
+ assert(cond); //# 41: compile-time error
}
void typeParametersNullableUses<I extends int, B extends bool, L extends List, F extends Function, S extends Stream>(
@@ -119,88 +121,88 @@
List<F?> funcList,
S? stream,
) async {
- x.isEven; //# 80: compile-time error
- x.round(); //# 81: compile-time error
- x.toString(); //# 82: ok
- x.hashCode; //# 83: ok
- x.runtimeType; //# 84: ok
- x.noSuchMethod(Invocation.method(#toString, [])); //# 85: ok
- x + 1; //# 86: compile-time error
- -x; //# 87: compile-time error
- x++; //# 88: compile-time error
- ++x; //# 89: compile-time error
- x..isEven; //# 90: compile-time error
- list[0]; //# 91: compile-time error
- list[0] = 0; //# 92: compile-time error
- x += 1; //# 93: compile-time error
- x ??= null; //# 94: ok
- x.round; //# 95: compile-time error
- x.toString; //# 96: ok
- x.noSuchMethod; //# 97: ok
- func(); //# 98: compile-time error
- funcList[0](); //# 99: compile-time error
- funcList.single(); //# 100: compile-time error
- throw x; //# 101: compile-time error
- cond || true; //# 102: compile-time error
- true || cond; //# 103: compile-time error
- cond && true; //# 104: compile-time error
- true && cond; //# 105: compile-time error
- !cond; //# 106: compile-time error
- cond ? null : null; //# 107: compile-time error
- if (cond) {} //# 108: compile-time error
- while (cond) {} //# 109: compile-time error
- for (;cond;) {} //# 110: compile-time error
- do {} while (cond); //# 111: compile-time error
- cond!; //# 112: ok
- cond ?? null; //# 113: ok
- cond == null; //# 114: ok
- cond != null; //# 115: ok
- x?.isEven; //# 116: ok
- x?.round(); //# 117: ok
- for(var i in list) {}; //# 118: compile-time error
- await for(var i in stream) {}; //# 119: compile-time error
+ x.isEven; //# 82: compile-time error
+ x.round(); //# 83: compile-time error
+ x.toString(); //# 84: ok
+ x.hashCode; //# 85: ok
+ x.runtimeType; //# 86: ok
+ x.noSuchMethod(Invocation.method(#toString, [])); //# 87: ok
+ x + 1; //# 88: compile-time error
+ -x; //# 89: compile-time error
+ x++; //# 90: compile-time error
+ ++x; //# 91: compile-time error
+ x..isEven; //# 92: compile-time error
+ list[0]; //# 93: compile-time error
+ list[0] = 0; //# 94: compile-time error
+ x += 1; //# 95: compile-time error
+ x ??= null; //# 96: ok
+ x.round; //# 97: compile-time error
+ x.toString; //# 98: ok
+ x.noSuchMethod; //# 99: ok
+ func(); //# 100: compile-time error
+ funcList[0](); //# 101: compile-time error
+ funcList.single(); //# 102: compile-time error
+ throw x; //# 103: compile-time error
+ cond || true; //# 104: compile-time error
+ true || cond; //# 105: compile-time error
+ cond && true; //# 106: compile-time error
+ true && cond; //# 107: compile-time error
+ !cond; //# 108: compile-time error
+ cond ? null : null; //# 109: compile-time error
+ if (cond) {} //# 110: compile-time error
+ while (cond) {} //# 111: compile-time error
+ for (;cond;) {} //# 112: compile-time error
+ do {} while (cond); //# 113: compile-time error
+ cond!; //# 114: ok
+ cond ?? null; //# 115: ok
+ cond == null; //# 116: ok
+ cond != null; //# 117: ok
+ x?.isEven; //# 118: ok
+ x?.round(); //# 119: ok
+ for(var i in list) {}; //# 120: compile-time error
+ await for(var i in stream) {}; //# 121: compile-time error
}
void dynamicUses() async {
dynamic dyn;
- dyn.isEven; //# 120: ok
- dyn.round(); //# 121: ok
- dyn.toString(); //# 122: ok
- dyn.hashCode; //# 123: ok
- dyn.runtimeType; //# 124: ok
- dyn.noSuchMethod(null); //# 125: ok
- dyn + 1; //# 126: ok
- -dyn; //# 127: ok
- dyn++; //# 128: ok
- ++dyn; //# 129: ok
- dyn..isEven; //# 130: ok
- dyn[0]; //# 131: ok
- dyn[0] = 0; //# 132: ok
- dyn += 1; //# 133: ok
- dyn ??= null; //# 134: ok
- dyn.round; //# 135: ok
- dyn.toString; //# 136: ok
- dyn.noSuchMethod; //# 137: ok
- dyn(); //# 138: ok
- dyn[0](); //# 139: ok
- dyn.single(); //# 140: ok
- throw dyn; //# 141: ok
- dyn || true; //# 142: ok
- true || dyn; //# 143: ok
- dyn && true; //# 144: ok
- true && dyn; //# 145: ok
- !dyn; //# 146: ok
- dyn ? null : null; //# 147: ok
- if (dyn) {} //# 148: ok
- while (dyn) {} //# 149: ok
- for (;dyn;) {} //# 150: ok
- do {} while (dyn); //# 151: ok
- dyn!; //# 152: ok
- dyn ?? null; //# 153: ok
- dyn == null; //# 154: ok
- dyn != null; //# 155: ok
- dyn?.isEven; //# 156: ok
- dyn?.round(); //# 157: ok
- for(var i in dyn) {}; //# 158: ok
- await for(var i in dyn) {}; //# 159: ok
+ dyn.isEven; //# 122: ok
+ dyn.round(); //# 123: ok
+ dyn.toString(); //# 124: ok
+ dyn.hashCode; //# 125: ok
+ dyn.runtimeType; //# 126: ok
+ dyn.noSuchMethod(null); //# 127: ok
+ dyn + 1; //# 128: ok
+ -dyn; //# 129: ok
+ dyn++; //# 130: ok
+ ++dyn; //# 131: ok
+ dyn..isEven; //# 132: ok
+ dyn[0]; //# 133: ok
+ dyn[0] = 0; //# 134: ok
+ dyn += 1; //# 135: ok
+ dyn ??= null; //# 136: ok
+ dyn.round; //# 137: ok
+ dyn.toString; //# 138: ok
+ dyn.noSuchMethod; //# 139: ok
+ dyn(); //# 140: ok
+ dyn[0](); //# 141: ok
+ dyn.single(); //# 142: ok
+ throw dyn; //# 143: ok
+ dyn || true; //# 144: ok
+ true || dyn; //# 145: ok
+ dyn && true; //# 146: ok
+ true && dyn; //# 147: ok
+ !dyn; //# 148: ok
+ dyn ? null : null; //# 149: ok
+ if (dyn) {} //# 150: ok
+ while (dyn) {} //# 151: ok
+ for (;dyn;) {} //# 152: ok
+ do {} while (dyn); //# 153: ok
+ dyn!; //# 154: ok
+ dyn ?? null; //# 155: ok
+ dyn == null; //# 156: ok
+ dyn != null; //# 157: ok
+ dyn?.isEven; //# 158: ok
+ dyn?.round(); //# 159: ok
+ for(var i in dyn) {}; //# 160: ok
+ await for(var i in dyn) {}; //# 161: ok
}