Test potentially-nullable access errors

Change-Id: Ia166dbb6fb353120e30555f60b7bea186537b05b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102242
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index a5e2927..1ea57d9 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -4660,8 +4660,8 @@
     if (expression == null ||
         !_isNonNullable ||
         expression.staticType == null ||
-        (expression.staticType as TypeImpl).nullabilitySuffix !=
-            NullabilitySuffix.question) {
+        expression.staticType.isDynamic ||
+        !_typeSystem.isPotentiallyNullable(expression.staticType)) {
       return false;
     }
 
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 19907e9..0fbc64d 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
@@ -219,6 +219,15 @@
 ''');
   }
 
+  test_member_dynamic_nullable() async {
+    await assertNoErrorsInCode(r'''
+m() {
+  dynamic x;
+  x.foo;
+}
+''');
+  }
+
   test_member_hashCode_nullable() async {
     await assertNoErrorsInCode(r'''
 m() {
@@ -273,6 +282,24 @@
 ''');
   }
 
+  test_member_potentiallyNullable() async {
+    await assertErrorCodesInCode(r'''
+m<T extends int?>() {
+  T x;
+  x.isEven;
+}
+''', [StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE]);
+  }
+
+  test_member_potentiallyNullable_called() async {
+    await assertErrorCodesInCode(r'''
+m<T extends Function>() {
+  List<T?> x;
+  x.first();
+}
+''', [StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE]);
+  }
+
   test_member_questionDot_nullable() async {
     await assertNoErrorsInCode(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 a991f55..ce24941 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
@@ -9,7 +9,6 @@
   int? x;
   bool? cond;
   List? list;
-  dynamic dyn;
   Function? func;
   List<Function?> funcList;
   Stream? stream;
@@ -51,8 +50,8 @@
   cond != null; //# 33: ok
   x?.isEven; //# 34: ok
   x?.round(); //# 35: ok
-  for(i in list) {}; //# 36: compile-time error
-  await for(i in stream) {}; //# 37: compile-time error
+  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
 }
 
@@ -60,3 +59,148 @@
   Iterable? iter;
   yield* iter; //# 39: compile-time error
 }
+
+void typeParametersNullableBounds<IQ extends int?, BQ extends bool?, LQ extends List?, FQ extends Function?, SQ extends Stream?>(
+    IQ x,
+    BQ cond,
+    LQ list,
+    FQ func,
+    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(null); //# 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 ??= null; //# 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
+}
+
+void typeParametersNullableUses<I extends int, B extends bool, L extends List, F extends Function, S extends Stream>(
+    I? x,
+    B? cond,
+    L? list,
+    F? func,
+    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(null); //# 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
+}
+
+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
+}