outdated --mode=null-safety, resolvable constrained to null-safe vers… (#2739)

From offline discussions we saw that this gives:
```
> env FLUTTER_ROOT=$HOME/projects/flutter dart ~/projects/pub/bin/pub.dart outdated --mode=null-safety
Showing dependencies that are currently not opted in to null-safety.
[✗] indicates versions without null safety support.
[✓] indicates versions opting in to null safety.

Package Name                 Current    Upgradable            Resolvable            Latest                

direct dependencies:        
adaptive_breakpoints         ✗0.0.3     ✗0.0.3                ✗0.0.3                ✗0.0.3                
animations                   ✗1.1.2     ✗1.1.2                ✗1.1.2                ✗1.1.2                
flare_dart                   ✗2.3.4     ✗2.3.4                ✗2.3.4                ✗2.3.4                
flare_flutter                ✗2.0.6     ✗2.0.6                ✗2.0.6                ✗2.0.6                
flutter_gallery_assets       ✗0.2.6     ✗0.2.6                ✗0.2.6                ✗0.2.6                
flutter_localized_locales    ✗1.1.2     ✓2.0.0-nullsafety     ✓2.0.0-nullsafety     ✓2.0.0-nullsafety     
flutter_staggered_grid_view  ✗0.3.2     ✗0.3.2                ✗0.3.2                ✗0.3.2                
google_fonts                 ✗1.1.1     ✗1.1.1                ✗1.1.1                ✗1.1.1                
intl                         ✗0.16.1    ✓0.17.0-nullsafety.2  ✓0.17.0-nullsafety.2  ✓0.17.0-nullsafety.2  
package_info                 ✗0.4.3+2   ✗0.4.3+2              ✗0.4.3+2              ✗0.4.3+2              
provider                     ✗4.3.2+2   ✗4.3.2+2              ✗4.3.2+2              ✗4.3.2+2              
rally_assets                 ✗2.0.0     ✗2.0.0                ✗2.0.0                ✗2.0.0                
scoped_model                 ✗1.1.0     ✗1.1.0                ✗1.1.0                ✗1.1.0                
shared_preferences           ✗0.5.12+4  ✗0.5.12+4             ✗0.5.12+4             ✗0.5.12+4             
shrine_images                ✗1.1.2     ✗1.1.2                ✗1.1.2                ✗1.1.2                
url_launcher                 ✗5.7.10    ✗5.7.10               ✓6.0.0-nullsafety.1   ✓6.0.0-nullsafety.1   

dev_dependencies:           
args                         ✗1.6.0     ✗1.6.0                ✗1.6.0                ✗1.6.0                
grinder                      ✗0.8.6     ✗0.8.6                ✗0.8.6                ✗0.8.6                
web_benchmarks               ✗0.0.2     ✗0.0.2                ✗0.0.2                ✗0.0.2                

2 upgradable dependencies are locked (in pubspec.lock) to older versions.
To update these dependencies, use `pub upgrade`.

1 dependency is constrained to a version that is older than a resolvable version.
To update it, edit pubspec.yaml.
```
which is ok,
Landing.

Travis failure seems to be an unrelated flake
diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart
index be0a6a5..f033036 100644
--- a/lib/src/command/outdated.dart
+++ b/lib/src/command/outdated.dart
@@ -105,6 +105,12 @@
 
   @override
   Future<void> runProtected() async {
+    final mode = <String, Mode>{
+      'outdated': _OutdatedMode(),
+      'null-safety': _NullSafetyMode(cache, entrypoint,
+          shouldShowSpinner: _shouldShowSpinner),
+    }[argResults['mode']];
+
     final includeDevDependencies = argResults['dev-dependencies'];
     final includeDependencyOverrides = argResults['dependency-overrides'];
     if (argResults['json'] && argResults.wasParsed('transitive')) {
@@ -120,7 +126,7 @@
         ? rootPubspec
         : stripDevDependencies(rootPubspec);
 
-    final resolvablePubspec = stripVersionUpperBounds(upgradablePubspec);
+    final resolvablePubspec = await mode.resolvablePubspec(upgradablePubspec);
 
     List<PackageId> upgradablePackages;
     List<PackageId> resolvablePackages;
@@ -221,11 +227,7 @@
     }
 
     rows.sort();
-    final mode = <String, Mode>{
-      'outdated': _OutdatedMode(),
-      'null-safety': _NullSafetyMode(cache, entrypoint,
-          shouldShowSpinner: _shouldShowSpinner),
-    }[argResults['mode']];
+
     final showAll = argResults['show-all'] || argResults['up-to-date'];
     if (argResults['json']) {
       await _outputJson(
@@ -587,6 +589,8 @@
   String get explanation;
   String get foundNoBadText;
   String get allGood;
+
+  Future<Pubspec> resolvablePubspec(Pubspec pubspec);
 }
 
 class _OutdatedMode implements Mode {
@@ -642,6 +646,11 @@
     }
     return rows;
   }
+
+  @override
+  Future<Pubspec> resolvablePubspec(Pubspec pubspec) async {
+    return stripVersionUpperBounds(pubspec);
+  }
 }
 
 class _NullSafetyMode implements Mode {
@@ -732,6 +741,11 @@
         ).toList()
     ];
   }
+
+  @override
+  Future<Pubspec> resolvablePubspec(Pubspec pubspec) async {
+    return constrainedToAtLeastNullSafetyPubspec(pubspec, cache);
+  }
 }
 
 /// Details about a single version of a package.
diff --git a/lib/src/pubspec_utils.dart b/lib/src/pubspec_utils.dart
index 032ce63..2341887 100644
--- a/lib/src/pubspec_utils.dart
+++ b/lib/src/pubspec_utils.dart
@@ -8,6 +8,7 @@
 import 'package_name.dart';
 import 'pubspec.dart';
 import 'source/hosted.dart';
+import 'system_cache.dart';
 
 /// Returns a new [Pubspec] without [original]'s dev_dependencies.
 Pubspec stripDevDependencies(Pubspec original) {
@@ -37,6 +38,65 @@
   );
 }
 
+Future<Pubspec> constrainedToAtLeastNullSafetyPubspec(
+    Pubspec original, SystemCache cache) async {
+  /// Get the first version of [package] opting in to null-safety.
+  Future<VersionRange> constrainToFirstWithNullSafety(
+      PackageRange packageRange) async {
+    final ref = packageRange.toRef();
+    final available = await cache.source(ref.source).getVersions(ref);
+    if (available.isEmpty) {
+      return stripUpperBound(packageRange.constraint);
+    }
+
+    available.sort((x, y) => x.version.compareTo(y.version));
+
+    for (final p in available) {
+      final pubspec = await cache.source(ref.source).describe(p);
+      if (pubspec.languageVersion.supportsNullSafety) {
+        return VersionRange(min: p.version, includeMin: true);
+      }
+    }
+    return stripUpperBound(packageRange.constraint);
+  }
+
+  Future<List<PackageRange>> allConstrainedToAtLeastNullSafety(
+    Map<String, PackageRange> constrained,
+  ) async {
+    final result = await Future.wait(constrained.keys.map((name) async {
+      final packageRange = constrained[name];
+      var unconstrainedRange = packageRange;
+
+      /// We only need to remove the upper bound if it is a hosted package.
+      if (packageRange.source is HostedSource) {
+        unconstrainedRange = PackageRange(
+            packageRange.name,
+            packageRange.source,
+            await constrainToFirstWithNullSafety(packageRange),
+            packageRange.description,
+            features: packageRange.features);
+      }
+      return unconstrainedRange;
+    }));
+
+    return result;
+  }
+
+  final constrainedLists = await Future.wait([
+    allConstrainedToAtLeastNullSafety(original.dependencies),
+    allConstrainedToAtLeastNullSafety(original.devDependencies),
+  ]);
+
+  return Pubspec(
+    original.name,
+    version: original.version,
+    sdkConstraints: original.sdkConstraints,
+    dependencies: constrainedLists[0],
+    devDependencies: constrainedLists[1],
+    dependencyOverrides: original.dependencyOverrides.values,
+  );
+}
+
 /// Returns new pubspec with the same dependencies as [original] but with the
 /// upper bounds of the constraints removed.
 ///
diff --git a/test/outdated/goldens/null_safety.txt b/test/outdated/goldens/null_safety.txt
index 212cc22..909c0cb 100644
--- a/test/outdated/goldens/null_safety.txt
+++ b/test/outdated/goldens/null_safety.txt
@@ -10,7 +10,7 @@
         "version": "1.0.0"
       },
       "resolvable": {
-        "version": "1.0.0"
+        "version": "2.0.0"
       },
       "latest": {
         "version": "2.0.0"
@@ -85,7 +85,7 @@
         "version": "1.0.0"
       },
       "resolvable": {
-        "version": "2.0.0"
+        "version": "2.0.0-nullsafety.0"
       },
       "latest": {
         "version": "2.0.0"
@@ -98,102 +98,102 @@
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
-Package Name                  Current  Upgradable  Resolvable  Latest  
+Package Name                  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:         
-bar                           *1.0.0   *1.0.0      *1.0.0      2.0.0   
-fails_analysis                *1.0.0   *1.0.0      2.0.0       2.0.0   
-fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_opts_out                 *1.0.0   *1.0.0      2.0.0       2.0.0   
-foo                           *1.0.0   *1.0.0      2.0.0       2.0.0   
+bar                           *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis                *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_opts_out                 *1.0.0   *1.0.0      2.0.0                2.0.0   
+foo                           *1.0.0   *1.0.0      *2.0.0-nullsafety.0  2.0.0   
 
-5  dependencies are constrained to versions that are older than a resolvable version.
+6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
-Package Name                  Current  Upgradable  Resolvable  Latest  
+Package Name                  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:         
-bar                           *1.0.0   *1.0.0      *1.0.0      2.0.0   
-fails_analysis                *1.0.0   *1.0.0      2.0.0       2.0.0   
-fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_opts_out                 *1.0.0   *1.0.0      2.0.0       2.0.0   
-foo                           *1.0.0   *1.0.0      2.0.0       2.0.0   
+bar                           *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis                *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_opts_out                 *1.0.0   *1.0.0      2.0.0                2.0.0   
+foo                           *1.0.0   *1.0.0      *2.0.0-nullsafety.0  2.0.0   
 
-5  dependencies are constrained to versions that are older than a resolvable version.
+6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
-Package Name                  Current  Upgradable  Resolvable  Latest  
+Package Name                  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:         
-bar                           *1.0.0   *1.0.0      *1.0.0      2.0.0   
-fails_analysis                *1.0.0   *1.0.0      2.0.0       2.0.0   
-fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_opts_out                 *1.0.0   *1.0.0      2.0.0       2.0.0   
-foo                           *1.0.0   *1.0.0      2.0.0       2.0.0   
+bar                           *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis                *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_opts_out                 *1.0.0   *1.0.0      2.0.0                2.0.0   
+foo                           *1.0.0   *1.0.0      *2.0.0-nullsafety.0  2.0.0   
 
-5  dependencies are constrained to versions that are older than a resolvable version.
+6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
-Package Name                  Current  Upgradable  Resolvable  Latest  
+Package Name                  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:         
-bar                           *1.0.0   *1.0.0      *1.0.0      2.0.0   
-fails_analysis                *1.0.0   *1.0.0      2.0.0       2.0.0   
-fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_opts_out                 *1.0.0   *1.0.0      2.0.0       2.0.0   
-foo                           *1.0.0   *1.0.0      2.0.0       2.0.0   
+bar                           *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis                *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_opts_out                 *1.0.0   *1.0.0      2.0.0                2.0.0   
+foo                           *1.0.0   *1.0.0      *2.0.0-nullsafety.0  2.0.0   
 
-5  dependencies are constrained to versions that are older than a resolvable version.
+6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
-Package Name                  Current  Upgradable  Resolvable  Latest  
+Package Name                  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:         
-bar                           *1.0.0   *1.0.0      *1.0.0      2.0.0   
-fails_analysis                *1.0.0   *1.0.0      2.0.0       2.0.0   
-fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_opts_out                 *1.0.0   *1.0.0      2.0.0       2.0.0   
-foo                           *1.0.0   *1.0.0      2.0.0       2.0.0   
+bar                           *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis                *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_opts_out                 *1.0.0   *1.0.0      2.0.0                2.0.0   
+foo                           *1.0.0   *1.0.0      *2.0.0-nullsafety.0  2.0.0   
 
-5  dependencies are constrained to versions that are older than a resolvable version.
+6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
-Package Name                  Current  Upgradable  Resolvable  Latest  
+Package Name                  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:         
-bar                           *1.0.0   *1.0.0      *1.0.0      2.0.0   
-fails_analysis                *1.0.0   *1.0.0      2.0.0       2.0.0   
-fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0       2.0.0   
-file_opts_out                 *1.0.0   *1.0.0      2.0.0       2.0.0   
-foo                           *1.0.0   *1.0.0      2.0.0       2.0.0   
+bar                           *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis                *1.0.0   *1.0.0      2.0.0                2.0.0   
+fails_analysis_in_dependency  *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_in_dependency_opts_out   *1.0.0   *1.0.0      2.0.0                2.0.0   
+file_opts_out                 *1.0.0   *1.0.0      2.0.0                2.0.0   
+foo                           *1.0.0   *1.0.0      *2.0.0-nullsafety.0  2.0.0   
 
-5  dependencies are constrained to versions that are older than a resolvable version.
+6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --mode=null-safety
@@ -201,42 +201,42 @@
 [✗] indicates versions without null safety support.
 [✓] indicates versions opting in to null safety.
 
-Package Name  Current  Upgradable  Resolvable  Latest  
+Package Name  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:
-bar           ✗1.0.0   ✗1.0.0      ✗1.0.0      ✓2.0.0  
-foo           ✗1.0.0   ✗1.0.0      ✓2.0.0      ✓2.0.0  
+bar           ✗1.0.0   ✗1.0.0      ✓2.0.0               ✓2.0.0  
+foo           ✗1.0.0   ✗1.0.0      ✓2.0.0-nullsafety.0  ✓2.0.0  
 
-1 dependency is constrained to a version that is older than a resolvable version.
-To update it, edit pubspec.yaml.
+2  dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
 [✓] indicates versions opting in to null safety.
 
-Package Name  Current  Upgradable  Resolvable  Latest  
+Package Name  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:
-bar           ✗1.0.0   ✗1.0.0      ✗1.0.0      ✓2.0.0  
-foo           ✗1.0.0   ✗1.0.0      ✓2.0.0      ✓2.0.0  
+bar           ✗1.0.0   ✗1.0.0      ✓2.0.0               ✓2.0.0  
+foo           ✗1.0.0   ✗1.0.0      ✓2.0.0-nullsafety.0  ✓2.0.0  
 
-1 dependency is constrained to a version that is older than a resolvable version.
-To update it, edit pubspec.yaml.
+2  dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
 [✓] indicates versions opting in to null safety.
 
-Package Name  Current  Upgradable  Resolvable  Latest  
+Package Name  Current  Upgradable  Resolvable           Latest  
 
 direct dependencies:
-bar           ✗1.0.0   ✗1.0.0      ✗1.0.0      ✓2.0.0  
-foo           ✗1.0.0   ✗1.0.0      ✓2.0.0      ✓2.0.0  
+bar           ✗1.0.0   ✗1.0.0      ✓2.0.0               ✓2.0.0  
+foo           ✗1.0.0   ✗1.0.0      ✓2.0.0-nullsafety.0  ✓2.0.0  
 
-1 dependency is constrained to a version that is older than a resolvable version.
-To update it, edit pubspec.yaml.
+2  dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml.
 
 $ pub outdated --json --mode=null-safety
 {
@@ -252,8 +252,8 @@
         "nullSafety": false
       },
       "resolvable": {
-        "version": "1.0.0",
-        "nullSafety": false
+        "version": "2.0.0",
+        "nullSafety": true
       },
       "latest": {
         "version": "2.0.0",
@@ -271,7 +271,7 @@
         "nullSafety": false
       },
       "resolvable": {
-        "version": "2.0.0",
+        "version": "2.0.0-nullsafety.0",
         "nullSafety": true
       },
       "latest": {
@@ -294,7 +294,7 @@
         "version": "1.0.0"
       },
       "resolvable": {
-        "version": "1.0.0"
+        "version": "2.0.0"
       },
       "latest": {
         "version": "2.0.0"
@@ -369,7 +369,7 @@
         "version": "1.0.0"
       },
       "resolvable": {
-        "version": "2.0.0"
+        "version": "2.0.0-nullsafety.0"
       },
       "latest": {
         "version": "2.0.0"
diff --git a/test/outdated/outdated_test.dart b/test/outdated/outdated_test.dart
index f10562b..d110142 100644
--- a/test/outdated/outdated_test.dart
+++ b/test/outdated/outdated_test.dart
@@ -206,6 +206,11 @@
         ..serve('bar', '1.0.0', pubspec: {
           'environment': {'sdk': '>=2.9.0 < 3.0.0'}
         })
+        ..serve('foo', '2.0.0-nullsafety.0', deps: {
+          'bar': '^2.0.0'
+        }, pubspec: {
+          'environment': {'sdk': '>=2.12.0 < 3.0.0'}
+        })
         ..serve('foo', '2.0.0', deps: {
           'bar': '^1.0.0'
         }, pubspec: {