Better FutureOr support, failing test.

Change-Id: Ib662286a58fb9389ce1065d87835613f3b899be4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106968
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index 9a902c2..879f4e7 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -65,11 +65,17 @@
 }
 
 /// Is [t] the top of the legacy type hierarch.
-bool _isLegacyTop(DartType t, {@required bool orTrueTop}) =>
-// TODO(mfairhurst): handle FutureOr<LegacyTop> cases, with tests.
-    (t.isObject &&
-        (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.none) ||
-    (orTrueTop ? _isTop(t) : false);
+bool _isLegacyTop(DartType t, {@required bool orTrueTop}) {
+  if (t.isDartAsyncFutureOr) {
+    return _isLegacyTop((t as InterfaceType).typeArguments[0],
+        orTrueTop: orTrueTop);
+  }
+  if (t.isObject &&
+      (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.none) {
+    return true;
+  }
+  return orTrueTop ? _isTop(t) : false;
+}
 
 bool _isTop(DartType t) {
   if (t.isDartAsyncFutureOr) {
@@ -631,6 +637,7 @@
     }
 
     // Legacy top case. Must be done now to find Object* <: Object.
+    // TODO: handle false positives like FutureOr<Object?>* and T* extends int?.
     if (t1.nullabilitySuffix == NullabilitySuffix.star &&
         _isLegacyTop(t2, orTrueTop: false)) {
       return true;
diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart
index d4792a5..abbf98e 100644
--- a/pkg/analyzer/test/generated/type_system_test.dart
+++ b/pkg/analyzer/test/generated/type_system_test.dart
@@ -2108,6 +2108,46 @@
     _checkGroups(dynamicType, equivalents: equivalents, subtypes: subtypes);
   }
 
+  @failingTest
+  void test_futureOr_topTypes() {
+    var objectStar =
+        (objectType as TypeImpl).withNullability(NullabilitySuffix.star);
+    var objectQuestion =
+        (objectType as TypeImpl).withNullability(NullabilitySuffix.question);
+    var futureOrObject = futureOrType.instantiate([objectType]);
+    var futureOrObjectStar = futureOrType.instantiate([objectStar]);
+    var futureOrObjectQuestion = futureOrType.instantiate([objectQuestion]);
+    var futureOrStarObject =
+        (futureOrObject as TypeImpl).withNullability(NullabilitySuffix.star);
+    var futureOrQuestionObject = (futureOrObject as TypeImpl)
+        .withNullability(NullabilitySuffix.question);
+    var futureOrStarObjectStar = (futureOrObjectStar as TypeImpl)
+        .withNullability(NullabilitySuffix.star);
+    var futureOrQuestionObjectStar = (futureOrObjectStar as TypeImpl)
+        .withNullability(NullabilitySuffix.question);
+    var futureOrStarObjectQuestion = (futureOrObjectQuestion as TypeImpl)
+        .withNullability(NullabilitySuffix.star);
+    var futureOrQuestionObjectQuestion = (futureOrObjectQuestion as TypeImpl)
+        .withNullability(NullabilitySuffix.question);
+
+    //FutureOr<Object> <: FutureOr*<Object?>
+    _checkGroups(futureOrObject, equivalents: [
+      objectStar,
+      futureOrObjectStar,
+      futureOrStarObject,
+      futureOrStarObjectStar,
+      objectType
+    ], subtypes: [], supertypes: [
+      objectQuestion,
+      futureOrQuestionObject,
+      futureOrObjectQuestion,
+      futureOrQuestionObject,
+      futureOrQuestionObjectStar,
+      futureOrStarObjectQuestion,
+      futureOrQuestionObjectQuestion
+    ]);
+  }
+
   void test_int_nullableTypes() {
     List<DartType> equivalents = <DartType>[
       intType,