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
 }