Pass --(no-)sound-null-safety arg through to VM. (#2542)

diff --git a/lib/src/command/global_run.dart b/lib/src/command/global_run.dart
index 82d6783..0d9bb94 100644
--- a/lib/src/command/global_run.dart
+++ b/lib/src/command/global_run.dart
@@ -30,8 +30,10 @@
     argParser.addFlag('checked', abbr: 'c', hide: true);
     argParser.addMultiOption('enable-experiment',
         help: 'Runs the executable in a VM with the given experiments enabled. '
-            '(Will disable snapshotting, resulting in slower startup)',
+            '(Will disable snapshotting, resulting in slower startup).',
         valueHelp: 'experiment');
+    argParser.addFlag('sound-null-safety',
+        help: 'Override the default null safety execution mode.');
     argParser.addOption('mode', help: 'Deprecated option', hide: true);
   }
 
@@ -62,8 +64,7 @@
       log.warning('The --mode flag is deprecated and has no effect.');
     }
 
-    final experiments = argResults['enable-experiment'] as List;
-    final vmArgs = vmArgFromExperiments(experiments);
+    final vmArgs = vmArgsFromArgResults(argResults);
     final globalEntrypoint = await globals.find(package);
     final exitCode = await runExecutable(globalEntrypoint,
         Executable.adaptProgramName(package, executable), args,
diff --git a/lib/src/command/run.dart b/lib/src/command/run.dart
index 8abfe67..7038c22 100644
--- a/lib/src/command/run.dart
+++ b/lib/src/command/run.dart
@@ -31,8 +31,10 @@
     argParser.addMultiOption('enable-experiment',
         help:
             'Runs the executable in a VM with the given experiments enabled.\n'
-            '(Will disable snapshotting, resulting in slower startup)',
+            '(Will disable snapshotting, resulting in slower startup).',
         valueHelp: 'experiment');
+    argParser.addFlag('sound-null-safety',
+        help: 'Override the default null safety execution mode.');
     argParser.addOption('mode', help: 'Deprecated option', hide: true);
     // mode exposed for `dartdev run` to use as subprocess.
     argParser.addFlag('dart-dev-run', hide: true);
@@ -72,8 +74,7 @@
       log.warning('The --mode flag is deprecated and has no effect.');
     }
 
-    final experiments = argResults['enable-experiment'] as List;
-    final vmArgs = vmArgFromExperiments(experiments);
+    final vmArgs = vmArgsFromArgResults(argResults);
 
     var exitCode = await runExecutable(
       entrypoint,
@@ -122,8 +123,7 @@
       args = argResults.rest.skip(1).toList();
     }
 
-    final experiments = argResults['enable-experiment'] as List;
-    final vmArgs = vmArgFromExperiments(experiments);
+    final vmArgs = vmArgsFromArgResults(argResults);
 
     return await flushThenExit(await runExecutable(
         entrypoint, Executable(package, 'bin/$command.dart'), args,
diff --git a/lib/src/executable.dart b/lib/src/executable.dart
index 04e5d78..b647c9c 100644
--- a/lib/src/executable.dart
+++ b/lib/src/executable.dart
@@ -6,6 +6,7 @@
 import 'dart:io';
 import 'dart:isolate';
 
+import 'package:args/args.dart';
 import 'package:path/path.dart' as p;
 import 'package:pedantic/pedantic.dart';
 
@@ -16,11 +17,16 @@
 import 'log.dart' as log;
 import 'utils.dart';
 
-/// Take a list of experiments to enable and turn them into a list with a single
-/// argument to pass to the VM enabling the same experiments.
-List<String> vmArgFromExperiments(List<String> experiments) {
+/// Code shared between `run` `global run` and `run --dartdev` for extracting
+/// vm arguments from arguments.
+List<String> vmArgsFromArgResults(ArgResults argResults) {
+  final experiments = argResults['enable-experiment'] as List;
   return [
-    if (experiments.isNotEmpty) "--enable-experiment=${experiments.join(',')}"
+    if (experiments.isNotEmpty) "--enable-experiment=${experiments.join(',')}",
+    if (argResults.wasParsed('sound-null-safety'))
+      argResults['sound-null-safety']
+          ? '--sound-null-safety'
+          : '--no-sound-null-safety',
   ];
 }
 
diff --git a/lib/src/null_safety_analysis.dart b/lib/src/null_safety_analysis.dart
index 976ab18..4fd15b7 100644
--- a/lib/src/null_safety_analysis.dart
+++ b/lib/src/null_safety_analysis.dart
@@ -6,7 +6,6 @@
 
 import 'package:analyzer/dart/analysis/context_builder.dart';
 import 'package:analyzer/dart/analysis/context_locator.dart';
-import 'package:analyzer/dart/ast/token.dart';
 import 'package:cli_util/cli_util.dart';
 import 'package:source_span/source_span.dart';
 
diff --git a/lib/src/validator.dart b/lib/src/validator.dart
index 83f8df1..280c2d3 100644
--- a/lib/src/validator.dart
+++ b/lib/src/validator.dart
@@ -5,7 +5,6 @@
 import 'dart:async';
 
 import 'package:meta/meta.dart';
-import 'package:pub/src/validator/null_safety_mixed_mode.dart';
 import 'package:pub_semver/pub_semver.dart';
 
 import 'entrypoint.dart';
@@ -22,6 +21,7 @@
 import 'validator/language_version.dart';
 import 'validator/license.dart';
 import 'validator/name.dart';
+import 'validator/null_safety_mixed_mode.dart';
 import 'validator/pubspec.dart';
 import 'validator/pubspec_field.dart';
 import 'validator/readme.dart';
diff --git a/test/global/run/errors_if_outside_bin_test.dart b/test/global/run/errors_if_outside_bin_test.dart
index 2078ae8..68eaa9d 100644
--- a/test/global/run/errors_if_outside_bin_test.dart
+++ b/test/global/run/errors_if_outside_bin_test.dart
@@ -27,7 +27,9 @@
     --enable-experiment=<experiment>    Runs the executable in a VM with the
                                         given experiments enabled. (Will disable
                                         snapshotting, resulting in slower
-                                        startup)
+                                        startup).
+    --[no-]sound-null-safety            Override the default null safety
+                                        execution mode.
 
 Run "pub help" to see global options.
 ''', exitCode: exit_codes.USAGE);
diff --git a/test/global/run/missing_executable_arg_test.dart b/test/global/run/missing_executable_arg_test.dart
index 47f2420..ebaa780 100644
--- a/test/global/run/missing_executable_arg_test.dart
+++ b/test/global/run/missing_executable_arg_test.dart
@@ -19,7 +19,9 @@
     --enable-experiment=<experiment>    Runs the executable in a VM with the
                                         given experiments enabled. (Will disable
                                         snapshotting, resulting in slower
-                                        startup)
+                                        startup).
+    --[no-]sound-null-safety            Override the default null safety
+                                        execution mode.
 
 Run "pub help" to see global options.
 ''', exitCode: exit_codes.USAGE);
diff --git a/test/run/dartdev/errors_if_path_in_dependency_test.dart b/test/run/dartdev/errors_if_path_in_dependency_test.dart
index 7081a04..c7e58d2 100644
--- a/test/run/dartdev/errors_if_path_in_dependency_test.dart
+++ b/test/run/dartdev/errors_if_path_in_dependency_test.dart
@@ -30,7 +30,9 @@
     --enable-experiment=<experiment>    Runs the executable in a VM with the
                                         given experiments enabled.
                                         (Will disable snapshotting, resulting in
-                                        slower startup)
+                                        slower startup).
+    --[no-]sound-null-safety            Override the default null safety
+                                        execution mode.
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
diff --git a/test/run/enable_experiments_test.dart b/test/run/enable_experiments_test.dart
index d241b9d..a859434 100644
--- a/test/run/enable_experiments_test.dart
+++ b/test/run/enable_experiments_test.dart
@@ -4,28 +4,83 @@
 
 import 'dart:io';
 
+import 'package:pub/src/language_version.dart';
+import 'package:pub_semver/pub_semver.dart';
 import 'package:test/test.dart';
 
 import '../descriptor.dart' as d;
 import '../test_pub.dart';
 
 void main() {
-  test('Succeeds running experimental code.', () async {
-    await d.dir(appPath, [
-      d.appPubspec(),
-      d.dir('bin', [
-        d.file('script.dart', '''
+  test(
+    'Succeeds running experimental code.',
+    () async {
+      await d.dir(appPath, [
+        d.appPubspec(),
+        d.dir('bin', [
+          d.file('script.dart', '''
   main() {
     int? a = int.tryParse('123');
   }
 ''')
-      ])
-    ]).create();
-    await pubGet();
-    await runPub(
-        args: ['run', '--enable-experiment=non-nullable', 'bin/script.dart']);
-  },
-      skip: Platform.version.contains('2.9')
-          ? false
-          : 'experiement non-nullable only available for test on sdk 2.9');
+        ])
+      ]).create();
+      await pubGet();
+      await runPub(
+          args: ['run', '--enable-experiment=non-nullable', 'bin/script.dart']);
+    },
+    skip: Platform.version.contains('2.9') || Platform.version.contains('2.10')
+        ? false
+        : 'experiement non-nullable only available for test on sdk 2.9',
+  );
+
+  test(
+    'Passes --no-sound-null-safety to the vm',
+    () async {
+      await d.dir(appPath, [
+        d.pubspec({
+          'name': 'test_package',
+          'environment': {'sdk': '>=2.10.0 <=3.0.0'}
+        }),
+        d.dir('bin', [
+          d.file('script.dart', '''
+import 'package:test_package/foo.dart';
+
+main() {
+  int? a = int.tryParse('123');
+  int b = p;
+}
+''')
+        ]),
+        d.dir(
+          'lib',
+          [
+            d.file('foo.dart', '''
+// @dart = 2.8
+int p = 10;
+'''),
+          ],
+        ),
+      ]).create();
+      await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '2.10.0'});
+      await runPub(args: [
+        'run',
+        '--no-sound-null-safety',
+        '--enable-experiment=non-nullable',
+        'bin/script.dart'
+      ], environment: {
+        '_PUB_TEST_SDK_VERSION': '2.10.0'
+      });
+      await runPub(
+          args: ['run', '--enable-experiment=non-nullable', 'bin/script.dart'],
+          environment: {'_PUB_TEST_SDK_VERSION': '2.10.0'},
+          error: contains("A library can't opt out of null safety by default"),
+          exitCode: 254);
+    },
+    skip: LanguageVersion.fromVersion(
+                Version.parse(Platform.version.split(' ').first)) >=
+            LanguageVersion.fromVersion(Version(2, 10, 0))
+        ? false
+        : '--sound-null-safety only available from sdk 2.10',
+  );
 }
diff --git a/test/run/errors_if_no_executable_is_given_test.dart b/test/run/errors_if_no_executable_is_given_test.dart
index 66201bd..55a103e 100644
--- a/test/run/errors_if_no_executable_is_given_test.dart
+++ b/test/run/errors_if_no_executable_is_given_test.dart
@@ -22,7 +22,9 @@
     --enable-experiment=<experiment>    Runs the executable in a VM with the
                                         given experiments enabled.
                                         (Will disable snapshotting, resulting in
-                                        slower startup)
+                                        slower startup).
+    --[no-]sound-null-safety            Override the default null safety
+                                        execution mode.
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
diff --git a/test/run/errors_if_path_in_dependency_test.dart b/test/run/errors_if_path_in_dependency_test.dart
index 6b109a1..3dc5721 100644
--- a/test/run/errors_if_path_in_dependency_test.dart
+++ b/test/run/errors_if_path_in_dependency_test.dart
@@ -30,7 +30,9 @@
     --enable-experiment=<experiment>    Runs the executable in a VM with the
                                         given experiments enabled.
                                         (Will disable snapshotting, resulting in
-                                        slower startup)
+                                        slower startup).
+    --[no-]sound-null-safety            Override the default null safety
+                                        execution mode.
 
 Run "pub help" to see global options.
 See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.