Tests for isNullable() and isNonNullable() and type parameters.

R=brianwilkerson@google.com

Change-Id: I120363de7067be8665995bbe2353f29240c9b961
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106601
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/dart/element/type_system.dart b/pkg/analyzer/lib/dart/element/type_system.dart
index df6cbe8..7906724 100644
--- a/pkg/analyzer/lib/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/dart/element/type_system.dart
@@ -94,7 +94,7 @@
   @experimental
   bool isPotentiallyNonNullable(DartType type);
 
-  /// Return `true` if the [type] is not a potentially nullable type.
+  /// Return `true` if the [type] is a potentially nullable type.
   ///
   /// We say that a type `T` is potentially nullable if `T` is not non-nullable.
   /// Note that this is different from saying that `T` is nullable. For example,
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index 4e222f6..53d06e1 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -2107,9 +2107,13 @@
       return false;
     } else if (type.isDartAsyncFutureOr) {
       isNonNullable((type as InterfaceType).typeArguments[0]);
+    } else if ((type as TypeImpl).nullabilitySuffix ==
+        NullabilitySuffix.question) {
+      return false;
+    } else if (type is TypeParameterType) {
+      return isNonNullable(type.bound);
     }
-    return (type as TypeImpl).nullabilitySuffix != NullabilitySuffix.question &&
-        (type is TypeParameterType ? isNonNullable(type.bound) : true);
+    return true;
   }
 
   @override
diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart
index c3df650..511ce30 100644
--- a/pkg/analyzer/test/generated/type_system_test.dart
+++ b/pkg/analyzer/test/generated/type_system_test.dart
@@ -22,6 +22,7 @@
 import 'package:analyzer/src/generated/testing/element_factory.dart';
 import 'package:analyzer/src/generated/testing/test_type_provider.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' show toUri;
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -2459,8 +2460,10 @@
 class TypeSystemTest extends AbstractTypeSystemTest {
   DartType get futureOrWithNoneType =>
       typeProvider.futureOrType.instantiate([noneType]);
+
   DartType get futureOrWithQuestionType =>
       typeProvider.futureOrType.instantiate([questionType]);
+
   DartType get futureOrWithStarType =>
       typeProvider.futureOrType.instantiate([starType]);
 
@@ -2505,6 +2508,51 @@
     expect(typeSystem.isNonNullable(starType), true);
   }
 
+  test_isNonNullable_typeParameter_noneBound_none() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeNone(bound: noneType),
+      ),
+      true,
+    );
+  }
+
+  test_isNonNullable_typeParameter_noneBound_question() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeQuestion(bound: noneType),
+      ),
+      false,
+    );
+  }
+
+  test_isNonNullable_typeParameter_questionBound_none() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeNone(bound: questionType),
+      ),
+      false,
+    );
+  }
+
+  test_isNonNullable_typeParameter_questionBound_question() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeQuestion(bound: questionType),
+      ),
+      false,
+    );
+  }
+
+  test_isNonNullable_typeParameter_starBound_star() {
+    expect(
+      typeSystem.isNonNullable(
+        typeParameterTypeStar(bound: starType),
+      ),
+      true,
+    );
+  }
+
   test_isNonNullable_void() {
     expect(typeSystem.isNonNullable(voidType), false);
   }
@@ -2541,6 +2589,51 @@
     expect(typeSystem.isNullable(starType), true);
   }
 
+  test_isNullable_typeParameter_noneBound_none() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeNone(bound: noneType),
+      ),
+      false,
+    );
+  }
+
+  test_isNullable_typeParameter_noneBound_question() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeQuestion(bound: noneType),
+      ),
+      true,
+    );
+  }
+
+  test_isNullable_typeParameter_questionBound_none() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeNone(bound: questionType),
+      ),
+      false,
+    );
+  }
+
+  test_isNullable_typeParameter_questionBound_question() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeQuestion(bound: questionType),
+      ),
+      true,
+    );
+  }
+
+  test_isNullable_typeParameter_starBound_star() {
+    expect(
+      typeSystem.isNullable(
+        typeParameterTypeStar(bound: starType),
+      ),
+      true,
+    );
+  }
+
   test_isNullable_void() {
     expect(typeSystem.isNullable(voidType), true);
   }
@@ -2617,4 +2710,28 @@
   test_isPotentiallyNullable_void() {
     expect(typeSystem.isPotentiallyNullable(voidType), true);
   }
+
+  DartType typeParameterTypeNone({@required DartType bound}) {
+    expect(bound, isNotNull);
+    var element = TypeParameterElementImpl.synthetic('T');
+    element.bound = bound;
+    return TypeParameterTypeImpl(element,
+        nullabilitySuffix: NullabilitySuffix.none);
+  }
+
+  DartType typeParameterTypeQuestion({@required DartType bound}) {
+    expect(bound, isNotNull);
+    var element = TypeParameterElementImpl.synthetic('T');
+    element.bound = bound;
+    return TypeParameterTypeImpl(element,
+        nullabilitySuffix: NullabilitySuffix.question);
+  }
+
+  DartType typeParameterTypeStar({@required DartType bound}) {
+    expect(bound, isNotNull);
+    var element = TypeParameterElementImpl.synthetic('T');
+    element.bound = bound;
+    return TypeParameterTypeImpl(element,
+        nullabilitySuffix: NullabilitySuffix.star);
+  }
 }