[pigeon] Remove support for non-NNBD (#1524)

Makes a breaking change to remove non-NNBD support, since it adds complexity
to the generator and there's no compelling reason to continue supporting it going
forward.

Also updates some legacy Dart commands in scripts (dartanalyzer, dart pub run)
to their modern versions.
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index a53b3b7..97bd5df 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 3.0.0
+
+* **BREAKING CHANGE**: Removes the `--dart_null_safety` flag. Generated Dart
+  now always uses nullability annotations, and thus requires Dart 2.12 or later.
+
 ## 2.0.4
 
 * Fixes bug where Dart `FlutterApi`s would assert that a nullable argument was nonnull.
@@ -9,7 +14,7 @@
 ## 2.0.2
 
 * Fixes Java crash for nullable nested type.
-  
+
 * ## 2.0.1
 
 * Adds support for TaskQueues for serial background execution.
diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart
index 0325c29..982044c 100644
--- a/packages/pigeon/lib/dart_generator.dart
+++ b/packages/pigeon/lib/dart_generator.dart
@@ -9,10 +9,7 @@
 /// Options that control how Dart code will be generated.
 class DartOptions {
   /// Constructor for DartOptions.
-  const DartOptions({this.isNullSafe = true, this.copyrightHeader});
-
-  /// Determines if the generated code has null safety annotations (Dart >=2.12 required).
-  final bool isNullSafe;
+  const DartOptions({this.copyrightHeader});
 
   /// A copyright header that will get prepended to generated code.
   final Iterable<String>? copyrightHeader;
@@ -23,7 +20,6 @@
     final Iterable<dynamic>? copyrightHeader =
         map['copyrightHeader'] as Iterable<dynamic>?;
     return DartOptions(
-      isNullSafe: map['isNullSafe'] as bool? ?? true,
       copyrightHeader: copyrightHeader?.cast<String>(),
     );
   }
@@ -32,7 +28,6 @@
   /// `x = DartOptions.fromMap(x.toMap())`.
   Map<String, Object> toMap() {
     final Map<String, Object> result = <String, Object>{
-      if (isNullSafe != null) 'isNullSafe': isNullSafe,
       if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!,
     };
     return result;
@@ -102,17 +97,17 @@
 }
 
 /// Creates a Dart type where all type arguments are [Objects].
-String _makeGenericTypeArguments(TypeDeclaration type, String nullTag) {
+String _makeGenericTypeArguments(TypeDeclaration type) {
   return type.typeArguments.isNotEmpty
-      ? '${type.baseName}<${type.typeArguments.map<String>((TypeDeclaration e) => 'Object$nullTag').join(', ')}>'
-      : _addGenericTypes(type, nullTag);
+      ? '${type.baseName}<${type.typeArguments.map<String>((TypeDeclaration e) => 'Object?').join(', ')}>'
+      : _addGenericTypes(type);
 }
 
 /// Creates a `.cast<>` call for an type. Returns an empty string if the
 /// type has no type arguments.
-String _makeGenericCastCall(TypeDeclaration type, String nullTag) {
+String _makeGenericCastCall(TypeDeclaration type) {
   return type.typeArguments.isNotEmpty
-      ? '.cast<${_flattenTypeArguments(type.typeArguments, nullTag)}>()'
+      ? '.cast<${_flattenTypeArguments(type.typeArguments)}>()'
       : '';
 }
 
@@ -125,16 +120,15 @@
     field.name.isEmpty ? 'arg$count' : field.name;
 
 /// Generates the arguments code for [func]
-/// Example: (func, getArgumentName, nullTag) -> 'String? foo, int bar'
+/// Example: (func, getArgumentName) -> 'String? foo, int bar'
 String _getMethodArgumentsSignature(
   Method func,
   String Function(int index, NamedType arg) getArgumentName,
-  String nullTag,
 ) {
   return func.arguments.isEmpty
       ? ''
       : indexMap(func.arguments, (int index, NamedType arg) {
-          final String type = _addGenericTypesNullable(arg.type, nullTag);
+          final String type = _addGenericTypesNullable(arg.type);
           final String argName = getArgumentName(index, arg);
           return '$type $argName';
         }).join(', ');
@@ -154,8 +148,6 @@
   final String codecName = _getCodecName(api);
   _writeCodec(indent, codecName, api, root);
   indent.addln('');
-  final String nullTag = opt.isNullSafe ? '?' : '';
-  final String unwrapOperator = opt.isNullSafe ? '!' : '';
   bool first = true;
   indent.write('class ${api.name} ');
   indent.scoped('{', '}', () {
@@ -163,9 +155,9 @@
 /// Constructor for [${api.name}].  The [binaryMessenger] named argument is
 /// available for dependency injection.  If it is left null, the default
 /// BinaryMessenger will be used which routes to the host platform.
-${api.name}({BinaryMessenger$nullTag binaryMessenger}) : _binaryMessenger = binaryMessenger;
+${api.name}({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger;
 
-final BinaryMessenger$nullTag _binaryMessenger;
+final BinaryMessenger? _binaryMessenger;
 ''');
 
     indent.writeln('static const MessageCodec<Object?> codec = $codecName();');
@@ -183,41 +175,39 @@
             _getSafeArgumentName(index, type);
         final Iterable<String> argNames = indexMap(func.arguments, argNameFunc);
         sendArgument = '<Object?>[${argNames.join(', ')}]';
-        argSignature = _getMethodArgumentsSignature(func, argNameFunc, nullTag);
+        argSignature = _getMethodArgumentsSignature(func, argNameFunc);
       }
       indent.write(
-        'Future<${_addGenericTypesNullable(func.returnType, nullTag)}> ${func.name}($argSignature) async ',
+        'Future<${_addGenericTypesNullable(func.returnType)}> ${func.name}($argSignature) async ',
       );
       indent.scoped('{', '}', () {
         final String channelName = makeChannelName(api, func);
         indent.writeln(
-            'final BasicMessageChannel<Object$nullTag> channel = BasicMessageChannel<Object$nullTag>(');
+            'final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(');
         indent.nest(2, () {
           indent.writeln(
             '\'$channelName\', codec, binaryMessenger: _binaryMessenger);',
           );
         });
-        final String returnType =
-            _makeGenericTypeArguments(func.returnType, nullTag);
-        final String castCall = _makeGenericCastCall(func.returnType, nullTag);
+        final String returnType = _makeGenericTypeArguments(func.returnType);
+        final String castCall = _makeGenericCastCall(func.returnType);
         const String accessor = 'replyMap[\'${Keys.result}\']';
-        final String unwrapper =
-            func.returnType.isNullable ? '' : unwrapOperator;
+        final String unwrapper = func.returnType.isNullable ? '' : '!';
         final String returnStatement = func.returnType.isVoid
             ? 'return;'
-            : 'return ($accessor as $returnType$nullTag)$unwrapper$castCall;';
+            : 'return ($accessor as $returnType?)$unwrapper$castCall;';
         indent.format('''
-final Map<Object$nullTag, Object$nullTag>$nullTag replyMap =\n\t\tawait channel.send($sendArgument) as Map<Object$nullTag, Object$nullTag>$nullTag;
+final Map<Object?, Object?>? replyMap =\n\t\tawait channel.send($sendArgument) as Map<Object?, Object?>?;
 if (replyMap == null) {
 \tthrow PlatformException(
 \t\tcode: 'channel-error',
 \t\tmessage: 'Unable to establish connection on channel.',
 \t);
 } else if (replyMap['error'] != null) {
-\tfinal Map<Object$nullTag, Object$nullTag> error = (replyMap['${Keys.error}'] as Map<Object$nullTag, Object$nullTag>$nullTag)$unwrapOperator;
+\tfinal Map<Object?, Object?> error = (replyMap['${Keys.error}'] as Map<Object?, Object?>?)!;
 \tthrow PlatformException(
-\t\tcode: (error['${Keys.errorCode}'] as String$nullTag)$unwrapOperator,
-\t\tmessage: error['${Keys.errorMessage}'] as String$nullTag,
+\t\tcode: (error['${Keys.errorCode}'] as String?)!,
+\t\tmessage: error['${Keys.errorMessage}'] as String?,
 \t\tdetails: error['${Keys.errorDetails}'],
 \t);''');
         // On iOS we can return nil from functions to accommodate error
@@ -260,8 +250,6 @@
   assert(api.location == ApiLocation.flutter);
   final String codecName = _getCodecName(api);
   _writeCodec(indent, codecName, api, root);
-  final String nullTag = opt.isNullSafe ? '?' : '';
-  final String unwrapOperator = opt.isNullSafe ? '!' : '';
   indent.write('abstract class ${api.name} ');
   indent.scoped('{', '}', () {
     indent.writeln('static const MessageCodec<Object?> codec = $codecName();');
@@ -269,23 +257,22 @@
     for (final Method func in api.methods) {
       final bool isAsync = func.isAsynchronous;
       final String returnType = isAsync
-          ? 'Future<${_addGenericTypesNullable(func.returnType, nullTag)}>'
-          : _addGenericTypesNullable(func.returnType, nullTag);
+          ? 'Future<${_addGenericTypesNullable(func.returnType)}>'
+          : _addGenericTypesNullable(func.returnType);
       final String argSignature = _getMethodArgumentsSignature(
         func,
         _getArgumentName,
-        nullTag,
       );
       indent.writeln('$returnType ${func.name}($argSignature);');
     }
     indent.write(
-        'static void setup(${api.name}$nullTag api, {BinaryMessenger$nullTag binaryMessenger}) ');
+        'static void setup(${api.name}? api, {BinaryMessenger? binaryMessenger}) ');
     indent.scoped('{', '}', () {
       for (final Method func in api.methods) {
         indent.write('');
         indent.scoped('{', '}', () {
           indent.writeln(
-            'final BasicMessageChannel<Object$nullTag> channel = BasicMessageChannel<Object$nullTag>(',
+            'final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(',
           );
           final String channelName = channelNameFunc == null
               ? makeChannelName(api, func)
@@ -304,14 +291,14 @@
           indent.add(' else ');
           indent.scoped('{', '}', () {
             indent.write(
-              'channel.$messageHandlerSetter((Object$nullTag message) async ',
+              'channel.$messageHandlerSetter((Object? message) async ',
             );
             indent.scoped('{', '});', () {
               final String returnType =
-                  _addGenericTypesNullable(func.returnType, nullTag);
+                  _addGenericTypesNullable(func.returnType);
               final bool isAsync = func.isAsynchronous;
               final String emptyReturnStatement = isMockHandler
-                  ? 'return <Object$nullTag, Object$nullTag>{};'
+                  ? 'return <Object?, Object?>{};'
                   : func.returnType.isVoid
                       ? 'return;'
                       : 'return null;';
@@ -325,19 +312,18 @@
                 );
                 const String argsArray = 'args';
                 indent.writeln(
-                    'final List<Object$nullTag> $argsArray = (message as List<Object$nullTag>$nullTag)$unwrapOperator;');
+                    'final List<Object?> $argsArray = (message as List<Object?>?)!;');
                 String argNameFunc(int index, NamedType type) =>
                     _getSafeArgumentName(index, type);
                 enumerate(func.arguments, (int count, NamedType arg) {
-                  final String argType = _addGenericTypes(arg.type, nullTag);
+                  final String argType = _addGenericTypes(arg.type);
                   final String argName = argNameFunc(count, arg);
                   final String genericArgType =
-                      _makeGenericTypeArguments(arg.type, nullTag);
-                  final String castCall =
-                      _makeGenericCastCall(arg.type, nullTag);
+                      _makeGenericTypeArguments(arg.type);
+                  final String castCall = _makeGenericCastCall(arg.type);
 
                   indent.writeln(
-                      'final $argType$nullTag $argName = ($argsArray[$count] as $genericArgType$nullTag)${castCall.isEmpty ? '' : '$nullTag$castCall'};');
+                      'final $argType? $argName = ($argsArray[$count] as $genericArgType?)${castCall.isEmpty ? '' : '?$castCall'};');
                   if (!arg.type.isNullable) {
                     indent.writeln(
                         'assert($argName != null, \'Argument for $channelName was null, expected non-null $argType.\');');
@@ -346,7 +332,7 @@
                 final Iterable<String> argNames =
                     indexMap(func.arguments, (int index, NamedType field) {
                   final String name = _getSafeArgumentName(index, field);
-                  return '$name${field.type.isNullable ? '' : unwrapOperator}';
+                  return '$name${field.type.isNullable ? '' : '!'}';
                 });
                 call = 'api.${func.name}(${argNames.join(', ')})';
               }
@@ -365,7 +351,7 @@
                 }
                 const String returnExpression = 'output';
                 final String returnStatement = isMockHandler
-                    ? 'return <Object$nullTag, Object$nullTag>{\'${Keys.result}\': $returnExpression};'
+                    ? 'return <Object?, Object?>{\'${Keys.result}\': $returnExpression};'
                     : 'return $returnExpression;';
                 indent.writeln(returnStatement);
               }
@@ -379,42 +365,40 @@
 
 /// Converts a [List] of [TypeDeclaration]s to a comma separated [String] to be
 /// used in Dart code.
-String _flattenTypeArguments(List<TypeDeclaration> args, String nullTag) {
+String _flattenTypeArguments(List<TypeDeclaration> args) {
   return args
       .map<String>((TypeDeclaration arg) => arg.typeArguments.isEmpty
-          ? '${arg.baseName}$nullTag'
-          : '${arg.baseName}<${_flattenTypeArguments(arg.typeArguments, nullTag)}>$nullTag')
+          ? '${arg.baseName}?'
+          : '${arg.baseName}<${_flattenTypeArguments(arg.typeArguments)}>?')
       .join(', ');
 }
 
 /// Creates the type declaration for use in Dart code from a [NamedType] making sure
 /// that type arguments are used for primitive generic types.
-String _addGenericTypes(TypeDeclaration type, String nullTag) {
+String _addGenericTypes(TypeDeclaration type) {
   final List<TypeDeclaration> typeArguments = type.typeArguments;
   switch (type.baseName) {
     case 'List':
       return (typeArguments.isEmpty)
-          ? 'List<Object$nullTag>'
-          : 'List<${_flattenTypeArguments(typeArguments, nullTag)}>';
+          ? 'List<Object?>'
+          : 'List<${_flattenTypeArguments(typeArguments)}>';
     case 'Map':
       return (typeArguments.isEmpty)
-          ? 'Map<Object$nullTag, Object$nullTag>'
-          : 'Map<${_flattenTypeArguments(typeArguments, nullTag)}>';
+          ? 'Map<Object?, Object?>'
+          : 'Map<${_flattenTypeArguments(typeArguments)}>';
     default:
       return type.baseName;
   }
 }
 
-String _addGenericTypesNullable(TypeDeclaration type, String nullTag) {
-  final String genericdType = _addGenericTypes(type, nullTag);
-  return type.isNullable ? '$genericdType$nullTag' : genericdType;
+String _addGenericTypesNullable(TypeDeclaration type) {
+  final String genericdType = _addGenericTypes(type);
+  return type.isNullable ? '$genericdType?' : genericdType;
 }
 
 /// Generates Dart source code for the given AST represented by [root],
 /// outputting the code to [sink].
 void generateDart(DartOptions opt, Root root, StringSink sink) {
-  final String nullTag = opt.isNullSafe ? '?' : '';
-  final String unwrapOperator = opt.isNullSafe ? '!' : '';
   final List<String> customClassNames =
       root.classes.map((Class x) => x.name).toList();
   final List<String> customEnumNames =
@@ -430,7 +414,7 @@
     indent.writeln(
       '// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name',
     );
-    indent.writeln('// @dart = ${opt.isNullSafe ? '2.12' : '2.8'}');
+    indent.writeln('// @dart = 2.12');
   }
 
   void writeEnums() {
@@ -471,17 +455,17 @@
       indent.write('Object encode() ');
       indent.scoped('{', '}', () {
         indent.writeln(
-          'final Map<Object$nullTag, Object$nullTag> pigeonMap = <Object$nullTag, Object$nullTag>{};',
+          'final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};',
         );
         for (final NamedType field in klass.fields) {
           indent.write('pigeonMap[\'${field.name}\'] = ');
           if (customClassNames.contains(field.type.baseName)) {
             indent.addln(
-              '${field.name} == null ? null : ${field.name}$unwrapOperator.encode();',
+              '${field.name}?.encode();',
             );
           } else if (customEnumNames.contains(field.type.baseName)) {
             indent.addln(
-              '${field.name} == null ? null : ${field.name}$unwrapOperator.index;',
+              '${field.name}?.index;',
             );
           } else {
             indent.addln('${field.name};');
@@ -496,32 +480,29 @@
         if (customClassNames.contains(field.type.baseName)) {
           indent.format('''
 pigeonMap['${field.name}'] != null
-\t\t? ${field.type.baseName}.decode(pigeonMap['${field.name}']$unwrapOperator)
+\t\t? ${field.type.baseName}.decode(pigeonMap['${field.name}']!)
 \t\t: null''', leadingSpace: false, trailingNewline: false);
         } else if (customEnumNames.contains(field.type.baseName)) {
           indent.format('''
 pigeonMap['${field.name}'] != null
-\t\t? ${field.type.baseName}.values[pigeonMap['${field.name}']$unwrapOperator as int]
+\t\t? ${field.type.baseName}.values[pigeonMap['${field.name}']! as int]
 \t\t: null''', leadingSpace: false, trailingNewline: false);
         } else if (field.type.typeArguments.isNotEmpty) {
-          final String genericType =
-              _makeGenericTypeArguments(field.type, nullTag);
-          final String castCall = _makeGenericCastCall(field.type, nullTag);
-          final String castCallPrefix =
-              field.type.isNullable ? nullTag : unwrapOperator;
+          final String genericType = _makeGenericTypeArguments(field.type);
+          final String castCall = _makeGenericCastCall(field.type);
+          final String castCallPrefix = field.type.isNullable ? '?' : '!';
           indent.add(
-            '(pigeonMap[\'${field.name}\'] as $genericType$nullTag)$castCallPrefix$castCall',
+            '(pigeonMap[\'${field.name}\'] as $genericType?)$castCallPrefix$castCall',
           );
         } else {
-          final String genericdType =
-              _addGenericTypesNullable(field.type, nullTag);
+          final String genericdType = _addGenericTypesNullable(field.type);
           if (field.type.isNullable) {
             indent.add(
               'pigeonMap[\'${field.name}\'] as $genericdType',
             );
           } else {
             indent.add(
-              'pigeonMap[\'${field.name}\']$unwrapOperator as $genericdType',
+              'pigeonMap[\'${field.name}\']! as $genericdType',
             );
           }
         }
@@ -532,7 +513,7 @@
       );
       indent.scoped('{', '}', () {
         indent.writeln(
-          'final Map<Object$nullTag, Object$nullTag> pigeonMap = message as Map<Object$nullTag, Object$nullTag>;',
+          'final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;',
         );
         indent.write('return ${klass.name}');
         indent.scoped('(', ');', () {
@@ -551,7 +532,7 @@
       writeConstructor();
       indent.addln('');
       for (final NamedType field in klass.fields) {
-        final String datatype = _addGenericTypesNullable(field.type, nullTag);
+        final String datatype = _addGenericTypesNullable(field.type);
         indent.writeln('$datatype ${field.name};');
       }
       if (klass.fields.isNotEmpty) {
@@ -602,7 +583,7 @@
     '// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis',
   );
   indent.writeln('// ignore_for_file: avoid_relative_lib_imports');
-  indent.writeln('// @dart = ${opt.isNullSafe ? '2.12' : '2.8'}');
+  indent.writeln('// @dart = 2.12');
   indent.writeln('import \'dart:async\';');
   indent.writeln(
     'import \'dart:typed_data\' show Uint8List, Int32List, Int64List, Float64List;',
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index f32f202..5b9ac85 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -8,7 +8,7 @@
 import 'ast.dart';
 
 /// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '2.0.4';
+const String pigeonVersion = '3.0.0';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index 85144d9..0b5e75e 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -1041,9 +1041,6 @@
     ..addOption('java_out', help: 'Path to generated Java file (.java).')
     ..addOption('java_package',
         help: 'The package that generated Java code will be in.')
-    ..addFlag('dart_null_safety',
-        help: 'Makes generated Dart code have null safety annotations',
-        defaultsTo: true)
     ..addOption('objc_header_out',
         help: 'Path to generated Objective-C header file (.h).')
     ..addOption('objc_prefix',
@@ -1082,9 +1079,6 @@
       javaOptions: JavaOptions(
         package: results['java_package'],
       ),
-      dartOptions: DartOptions(
-        isNullSafe: results['dart_null_safety'],
-      ),
       copyrightHeader: results['copyright_header'],
       oneLanguage: results['one_language'],
       astOut: results['ast_out'],
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index 8f78999..b7911ae 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
 repository: https://github.com/flutter/packages/tree/main/packages/pigeon
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 2.0.4 # This must match the version in lib/generator_tools.dart
+version: 3.0.0 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/pigeon/run_tests.sh b/packages/pigeon/run_tests.sh
index 5e311ea..be5cd99 100755
--- a/packages/pigeon/run_tests.sh
+++ b/packages/pigeon/run_tests.sh
@@ -86,38 +86,26 @@
 # test_null_safe_dart(<path to pigeon file>)
 #
 # Compiles the pigeon file to a temp directory and attempts to run the dart
-# analyzer on it with and without null safety turned on.
+# analyzer on it.
 test_pigeon_dart() {
   echo "test_pigeon_dart($1)"
-  temp_dir_1=$(mktmpdir)
-  temp_dir_2=$(mktmpdir)
+  temp_dir=$(mktmpdir)
 
   $run_pigeon \
     --input $1 \
-    --dart_out $temp_dir_1/pigeon.dart &
-  null_safe_gen_pid=$!
+    --dart_out $temp_dir/pigeon.dart &
+  gen_pid=$!
 
-  $run_pigeon \
-    --no-dart_null_safety \
-    --input $1 \
-    --dart_out $temp_dir_2/pigeon.dart &
-  non_null_safe_gen_pid=$!
-
-  wait $null_safe_gen_pid
-  wait $non_null_safe_gen_pid
+  wait $gen_pid
 
   # `./e2e_tests/test_objc/.packages` is used to get access to Flutter since
   # Pigeon doesn't depend on Flutter.
-  dartanalyzer $temp_dir_1/pigeon.dart --fatal-infos --fatal-warnings --packages ./e2e_tests/test_objc/.packages &
-  null_safe_analyze_pid=$!
-  dartanalyzer $temp_dir_2/pigeon.dart --fatal-infos --fatal-warnings --packages ./e2e_tests/test_objc/.packages &
-  non_null_safe_analyze_pid=$!
+  dart analyze $temp_dir/pigeon.dart --fatal-infos --fatal-warnings --packages ./e2e_tests/test_objc/.packages &
+  analyze_pid=$!
 
-  wait $null_safe_analyze_pid
-  wait $non_null_safe_analyze_pid
+  wait $analyze_pid
 
-  rm -rf $temp_dir_1
-  rm -rf $temp_dir_2
+  rm -rf $temp_dir
 }
 
 print_usage() {
@@ -184,7 +172,7 @@
 }
 
 run_dart_unittests() {
-  dart pub run pigeon:run_tests -t dart_unittests
+  dart run pigeon:run_tests -t dart_unittests
 }
 
 test_command_line() {
@@ -204,11 +192,11 @@
 }
 
 run_flutter_unittests() {
-  dart pub run pigeon:run_tests -t flutter_unittests
+  dart run pigeon:run_tests -t flutter_unittests
 }
 
 run_mock_handler_tests() {
-  dart pub run pigeon:run_tests -t mock_handler_tests
+  dart run pigeon:run_tests -t mock_handler_tests
 }
 
 run_dart_compilation_tests() {
diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart
index 00c8158..4f03de6 100644
--- a/packages/pigeon/test/dart_generator_test.dart
+++ b/packages/pigeon/test/dart_generator_test.dart
@@ -27,10 +27,10 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('class Foobar'));
-    expect(code, contains('  dataType1 field1;'));
+    expect(code, contains('  dataType1? field1;'));
   });
 
   test('gen one enum', () {
@@ -47,7 +47,7 @@
       enums: <Enum>[anEnum],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('enum Foobar'));
     expect(code, contains('  one,'));
@@ -94,7 +94,7 @@
       ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('class Api'));
     expect(code, contains('Future<Output> doSomething(Input arg_input)'));
@@ -121,7 +121,7 @@
       ])
     ], classes: <Class>[], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('class Api'));
     expect(code, contains('Future<int> add(int arg_x, int arg_y)'));
@@ -149,7 +149,7 @@
       ])
     ], classes: <Class>[], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('class Api'));
     expect(code, contains('int add(int x, int y)'));
@@ -188,18 +188,18 @@
       )
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(
       code,
       contains(
-        'pigeonMap[\'nested\'] = nested == null ? null : nested.encode()',
+        'pigeonMap[\'nested\'] = nested?.encode()',
       ),
     );
     expect(
       code.replaceAll('\n', ' ').replaceAll('  ', ''),
       contains(
-        'nested: pigeonMap[\'nested\'] != null ? Input.decode(pigeonMap[\'nested\']) : null',
+        'nested: pigeonMap[\'nested\'] != null ? Input.decode(pigeonMap[\'nested\']!) : null',
       ),
     );
   });
@@ -244,7 +244,7 @@
       ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('abstract class Api'));
     expect(code, contains('static void setup(Api'));
@@ -281,7 +281,7 @@
       ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('Future<void> doSomething'));
     expect(code, contains('return;'));
@@ -317,7 +317,7 @@
       ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     // The next line verifies that we're not setting a variable to the value of "doSomething", but
     // ignores the line where we assert the value of the argument isn't null, since on that line
@@ -349,7 +349,7 @@
       ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, matches('output.*=.*doSomething[(][)]'));
     expect(code, contains('Output doSomething();'));
@@ -394,59 +394,9 @@
       )
     ]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
-    expect(code,
-        contains('pigeonMap[\'enum1\'] = enum1 == null ? null : enum1.index;'));
-    expect(code, contains('? Enum.values[pigeonMap[\'enum1\'] as int]'));
-    expect(code, contains('EnumClass doSomething(EnumClass arg0);'));
-  });
-
-  test('flutter enum argument with enum class nullsafe', () {
-    final Root root = Root(apis: <Api>[
-      Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
-        Method(
-          name: 'doSomething',
-          arguments: <NamedType>[
-            NamedType(
-                type: const TypeDeclaration(
-                  baseName: 'EnumClass',
-                  isNullable: false,
-                ),
-                name: '',
-                offset: null)
-          ],
-          returnType:
-              const TypeDeclaration(baseName: 'EnumClass', isNullable: false),
-          isAsynchronous: false,
-        )
-      ])
-    ], classes: <Class>[
-      Class(name: 'EnumClass', fields: <NamedType>[
-        NamedType(
-            type: const TypeDeclaration(
-              baseName: 'Enum',
-              isNullable: true,
-            ),
-            name: 'enum1',
-            offset: null)
-      ]),
-    ], enums: <Enum>[
-      Enum(
-        name: 'Enum',
-        members: <String>[
-          'one',
-          'two',
-        ],
-      )
-    ]);
-    final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
-    final String code = sink.toString();
-    expect(
-        code,
-        contains(
-            'pigeonMap[\'enum1\'] = enum1 == null ? null : enum1!.index;'));
+    expect(code, contains('pigeonMap[\'enum1\'] = enum1?.index;'));
     expect(code, contains('? Enum.values[pigeonMap[\'enum1\']! as int]'));
     expect(code, contains('EnumClass doSomething(EnumClass arg0);'));
   });
@@ -474,7 +424,7 @@
       ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, matches('channel.send[(]null[)]'));
   });
@@ -538,7 +488,7 @@
     ], enums: <Enum>[]);
     final StringBuffer mainCodeSink = StringBuffer();
     final StringBuffer testCodeSink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, mainCodeSink);
+    generateDart(const DartOptions(), root, mainCodeSink);
     final String mainCode = mainCodeSink.toString();
     expect(mainCode, isNot(contains('import \'fo\\\'o.dart\';')));
     expect(mainCode, contains('class Api {'));
@@ -546,39 +496,14 @@
     expect(mainCode, isNot(contains('.ApiMock.doSomething')));
     expect(mainCode, isNot(contains('\'${Keys.result}\': output')));
     expect(mainCode, isNot(contains('return <Object, Object>{};')));
-    generateTestDart(
-        const DartOptions(isNullSafe: false), root, testCodeSink, "fo'o.dart");
+    generateTestDart(const DartOptions(), root, testCodeSink, "fo'o.dart");
     final String testCode = testCodeSink.toString();
     expect(testCode, contains('import \'fo\\\'o.dart\';'));
     expect(testCode, isNot(contains('class Api {')));
     expect(testCode, contains('abstract class ApiMock'));
     expect(testCode, isNot(contains('.ApiMock.doSomething')));
     expect(testCode, contains('\'${Keys.result}\': output'));
-    expect(testCode, contains('return <Object, Object>{};'));
-  });
-
-  test('opt out of nndb', () {
-    final Class klass = Class(
-      name: 'Foobar',
-      fields: <NamedType>[
-        NamedType(
-            type: const TypeDeclaration(
-              baseName: 'dataType1',
-              isNullable: true,
-            ),
-            name: 'field1',
-            offset: null),
-      ],
-    );
-    final Root root = Root(
-      apis: <Api>[],
-      classes: <Class>[klass],
-      enums: <Enum>[],
-    );
-    final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
-    final String code = sink.toString();
-    expect(code, contains('// @dart = 2.8'));
+    expect(testCode, contains('return <Object?, Object?>{};'));
   });
 
   test('gen one async Flutter Api', () {
@@ -621,12 +546,12 @@
       ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('abstract class Api'));
     expect(code, contains('Future<Output> doSomething(Input arg0);'));
     expect(
-        code, contains('final Output output = await api.doSomething(arg0);'));
+        code, contains('final Output output = await api.doSomething(arg0!);'));
   });
 
   test('gen one async Flutter Api with void return', () {
@@ -668,7 +593,7 @@
       ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, isNot(matches('=.s*doSomething')));
     expect(code, contains('await api.doSomething('));
@@ -715,7 +640,7 @@
       ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('class Api'));
     expect(code, matches('Output.*doSomething.*Input'));
@@ -744,7 +669,7 @@
       ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: false), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, matches('channel.send[(]null[)]'));
   });
@@ -757,8 +682,7 @@
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(
-      DartOptions(
-          isNullSafe: false, copyrightHeader: _makeIterable('hello world')),
+      DartOptions(copyrightHeader: _makeIterable('hello world')),
       root,
       sink,
     );
@@ -787,7 +711,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('class Foobar'));
     expect(code, contains('  List<int?>? field1;'));
@@ -815,7 +739,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('class Foobar'));
     expect(code, contains('  Map<String?, int?>? field1;'));
@@ -845,7 +769,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('doit(List<int?> arg'));
   });
@@ -874,7 +798,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('doit(List<int?> arg'));
   });
@@ -898,7 +822,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('Future<List<int?>> doit('));
     expect(
@@ -936,7 +860,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('List<int?> doit('));
     expect(
@@ -963,7 +887,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('Future<int?> doit()'));
     expect(code, contains('return (replyMap[\'result\'] as int?);'));
@@ -987,7 +911,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('Future<int?> doit()'));
     expect(code, contains('return (replyMap[\'result\'] as int?);'));
@@ -1010,7 +934,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('int? doit();'));
     expect(code, contains('final int? output = api.doit();'));
@@ -1034,7 +958,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('Future<int?> doit();'));
     expect(code, contains('final int? output = await api.doit();'));
@@ -1057,7 +981,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(
         code,
@@ -1086,7 +1010,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('Future<void> doit(int? arg_foo) async {'));
   });
@@ -1112,7 +1036,7 @@
       enums: <Enum>[],
     );
     final StringBuffer sink = StringBuffer();
-    generateDart(const DartOptions(isNullSafe: true), root, sink);
+    generateDart(const DartOptions(), root, sink);
     final String code = sink.toString();
     expect(code, contains('void doit(int? foo);'));
   });
diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart
index 6d6447c..d40c015 100644
--- a/packages/pigeon/test/pigeon_lib_test.dart
+++ b/packages/pigeon/test/pigeon_lib_test.dart
@@ -329,12 +329,6 @@
     expect(classNames.contains('OnlyVisibleFromNesting'), true);
   });
 
-  test('null safety flag', () {
-    final PigeonOptions results =
-        Pigeon.parseArgs(<String>['--dart_null_safety']);
-    expect(results.dartOptions?.isNullSafe, isTrue);
-  });
-
   test('copyright flag', () {
     final PigeonOptions results =
         Pigeon.parseArgs(<String>['--copyright_header', 'foobar.txt']);
@@ -423,7 +417,7 @@
 @HostApi()
 abstract class NotificationsHostApi {
   void doit(Foo foo);
-}  
+}
 ''';
     final ParseResults results = _parseSource(code);
     expect(results.errors.length, 0);
@@ -462,7 +456,7 @@
   test('test field initialization', () {
     const String code = '''
 class Foo {
-  int? x = 123;  
+  int? x = 123;
 }
 
 @HostApi()