[pigeon] added more errors for improper usage (#393)

diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index ac7625e..b365f8b 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 * [front-end] Added a more explicit error if generic fields are used.
 * [front-end] Added a more explicit error for static fields.
+* [front-end] Added more errors for incorrect usage of Pigeon (previously they were just ignored).
 
 ## 0.3.0
 
diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart
index 4d78eca..bfc438f 100644
--- a/packages/pigeon/lib/ast.dart
+++ b/packages/pigeon/lib/ast.dart
@@ -21,7 +21,10 @@
     required this.name,
     required this.returnType,
     required this.argType,
+    this.isArgNullable = false,
     this.isAsynchronous = false,
+    this.isReturnNullable = false,
+    this.offset,
   });
 
   /// The name of the method.
@@ -30,12 +33,21 @@
   /// The data-type of the return value.
   String returnType;
 
+  /// True if the method can return a null value.
+  bool isReturnNullable;
+
   /// The data-type of the argument.
   String argType;
 
+  /// True if the argument has a null tag `?`.
+  bool isArgNullable;
+
   /// Whether the receiver of this method is expected to return synchronously or not.
   bool isAsynchronous;
 
+  /// The offset in the source file where the field appears.
+  int? offset;
+
   @override
   String toString() {
     return '(Api name:$name returnType:$returnType argType:$argType isAsynchronous:$isAsynchronous)';
@@ -76,6 +88,8 @@
   Field({
     required this.name,
     required this.dataType,
+    required this.isNullable,
+    this.typeArguments,
     this.offset,
   });
 
@@ -88,6 +102,12 @@
   /// The offset in the source file where the field appears.
   int? offset;
 
+  /// True if the datatype is nullable (ex `int?`).
+  bool isNullable;
+
+  /// Type parameters used for generics.
+  List<Field>? typeArguments;
+
   @override
   String toString() {
     return '(Field name:$name dataType:$dataType)';
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index b4dc9a1..af64799 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -391,7 +391,7 @@
   final List<String> customEnums = root.enums.map((Enum x) => x.name).toList();
   for (final Class klass in root.classes) {
     for (final Field field in klass.fields) {
-      if (field.dataType.contains('<')) {
+      if (field.typeArguments != null) {
         result.add(Error(
           message:
               'Unsupported datatype:"${field.dataType}" in class "${klass.name}". Generic fields aren\'t yet supported (https://github.com/flutter/flutter/issues/63468).',
@@ -410,15 +410,33 @@
   }
   for (final Api api in root.apis) {
     for (final Method method in api.methods) {
+      if (method.isReturnNullable) {
+        result.add(Error(
+          message:
+              'Nullable return types types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}',
+          lineNumber: _calculateLineNumberNullable(source, method.offset),
+        ));
+      }
+      if (method.isArgNullable) {
+        result.add(Error(
+          message:
+              'Nullable argument types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}',
+          lineNumber: _calculateLineNumberNullable(source, method.offset),
+        ));
+      }
       if (_validTypes.contains(method.argType)) {
         result.add(Error(
-            message:
-                'Unsupported argument type: "${method.argType}" in API: "${api.name}" method: "${method.name}'));
+          message:
+              'Primitive argument types aren\'t yet supported (https://github.com/flutter/flutter/issues/66467): "${method.argType}" in API: "${api.name}" method: "${method.name}',
+          lineNumber: _calculateLineNumberNullable(source, method.offset),
+        ));
       }
       if (_validTypes.contains(method.returnType)) {
         result.add(Error(
-            message:
-                'Unsupported return type: "${method.returnType}" in API: "${api.name}" method: "${method.name}'));
+          message:
+              'Primitive return types aren\'t yet supported (https://github.com/flutter/flutter/issues/66467): "${method.returnType}" in API: "${api.name}" method: "${method.name}',
+          lineNumber: _calculateLineNumberNullable(source, method.offset),
+        ));
       }
     }
   }
@@ -426,6 +444,17 @@
   return result;
 }
 
+class _FindInitializer extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
+  dart_ast.Expression? initializer;
+  @override
+  Object? visitVariableDeclaration(dart_ast.VariableDeclaration node) {
+    if (node.initializer != null) {
+      initializer = node.initializer;
+    }
+    return null;
+  }
+}
+
 class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
   _RootBuilder(this.source, this.ignoresInvalidImports);
 
@@ -472,30 +501,11 @@
       }
     }
 
-    final List<Class> classesWithNullTagStripped = _classes.map((Class aClass) {
-      return Class(
-          name: aClass.name,
-          fields: aClass.fields.map((Field field) {
-            String datatype = field.dataType;
-            if (datatype.endsWith('?')) {
-              datatype = datatype.substring(0, datatype.length - 1);
-            } else {
-              // TODO(aaclarke): Provide an error when not using a nullable type.
-              // _errors.add(Error(
-              //     message:
-              //         'Field ${aClass.name}.${field.name} must be nullable.'));
-            }
-            return Field(
-                name: field.name, dataType: datatype, offset: field.offset);
-          }).toList());
-    }).toList();
-
     final List<String> classesToCheck = List<String>.from(referencedTypes);
     while (classesToCheck.isNotEmpty) {
       final String next = classesToCheck.last;
       classesToCheck.removeLast();
-      final Class aClass = classesWithNullTagStripped.firstWhere(
-          (Class x) => x.name == next,
+      final Class aClass = _classes.firstWhere((Class x) => x.name == next,
           orElse: () => Class(name: '', fields: <Field>[]));
       for (final Field field in aClass.fields) {
         if (!referencedTypes.contains(field.dataType) &&
@@ -510,8 +520,7 @@
         ? (Class x) => !referencedTypes.contains(x.name)
         : (Class x) =>
             !referencedTypes.contains(x.name) && !typeFilter.contains(x.name);
-    final List<Class> referencedClasses =
-        List<Class>.from(classesWithNullTagStripped);
+    final List<Class> referencedClasses = List<Class>.from(_classes);
     referencedClasses.removeWhere(classRemover);
 
     final List<Enum> referencedEnums = List<Enum>.from(_enums);
@@ -646,6 +655,7 @@
   Object? visitMethodDeclaration(dart_ast.MethodDeclaration node) {
     final dart_ast.FormalParameterList parameters = node.parameters!;
     late String argType;
+    bool isNullable = false;
     if (parameters.parameters.isEmpty) {
       argType = 'void';
     } else {
@@ -655,6 +665,7 @@
           // ignore: always_specify_types
           .firstWhere((e) => e is dart_ast.TypeName) as dart_ast.TypeName;
       argType = typeName.name.name;
+      isNullable = typeName.question != null;
     }
     final bool isAsynchronous = _hasMetadata(node.metadata, 'async');
     if (_currentApi != null) {
@@ -662,7 +673,15 @@
           name: node.name.name,
           returnType: node.returnType.toString(),
           argType: argType,
-          isAsynchronous: isAsynchronous));
+          isReturnNullable: node.returnType!.question != null,
+          isArgNullable: isNullable,
+          isAsynchronous: isAsynchronous,
+          offset: node.offset));
+    } else if (_currentClass != null) {
+      _errors.add(Error(
+          message:
+              'Methods aren\'t supported in Pigeon data classes ("${node.name.name}").',
+          lineNumber: _calculateLineNumber(source, node.offset)));
     }
     node.visitChildren(this);
     return null;
@@ -689,20 +708,59 @@
                 'Pigeon doesn\'t support static fields ("${node.toString()}"), consider using enums.',
             lineNumber: _calculateLineNumber(source, node.offset)));
       } else if (type is dart_ast.NamedType) {
-        _currentClass!.fields.add(Field(
-          name: node.fields.variables[0].name.name,
-          dataType: type.toString(),
-          offset: node.offset,
-        ));
+        final _FindInitializer findInitializerVisitor = _FindInitializer();
+        node.visitChildren(findInitializerVisitor);
+        if (findInitializerVisitor.initializer != null) {
+          _errors.add(Error(
+              message:
+                  'Initialization isn\'t supported for fields in Pigeon data classes ("$node"), just use nullable types with no initializer (example "int? x;").',
+              lineNumber: _calculateLineNumber(source, node.offset)));
+        } else {
+          final dart_ast.TypeArgumentList? typeArguments = type.typeArguments;
+          _currentClass!.fields.add(Field(
+            name: node.fields.variables[0].name.name,
+            dataType: type.name.name,
+            isNullable: type.question != null,
+            // TODO(aaclarke): This probably has to be recursive at some point.
+            // ignore: prefer_null_aware_operators
+            typeArguments: typeArguments == null
+                ? null
+                : typeArguments.arguments
+                    .map((dart_ast.TypeAnnotation e) => Field(
+                          name: '',
+                          dataType: (e.childEntities.first
+                                  as dart_ast.SimpleIdentifier)
+                              .name,
+                          isNullable: e.question != null,
+                          offset: e.offset,
+                        ))
+                    .toList(),
+            offset: node.offset,
+          ));
+        }
       } else {
         _errors.add(Error(
             message: 'Expected a named type but found "${node.toString()}".',
             lineNumber: _calculateLineNumber(source, node.offset)));
       }
+    } else if (_currentApi != null) {
+      _errors.add(Error(
+          message: 'Fields aren\'t supported in Pigeon API classes ("$node").',
+          lineNumber: _calculateLineNumber(source, node.offset)));
     }
     node.visitChildren(this);
     return null;
   }
+
+  @override
+  Object? visitConstructorDeclaration(dart_ast.ConstructorDeclaration node) {
+    final String type = _currentApi != null ? 'API classes' : 'data classes';
+    _errors.add(Error(
+        message: 'Constructors aren\'t supported in $type ("$node").',
+        lineNumber: _calculateLineNumber(source, node.offset)));
+    node.visitChildren(this);
+    return null;
+  }
 }
 
 int? _calculateLineNumberNullable(String contents, int? offset) {
diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart
index 5c52a3e..156603c 100644
--- a/packages/pigeon/test/dart_generator_test.dart
+++ b/packages/pigeon/test/dart_generator_test.dart
@@ -15,6 +15,7 @@
         Field(
           name: 'field1',
           dataType: 'dataType1',
+          isNullable: true,
         ),
       ],
     );
@@ -57,17 +58,26 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -80,11 +90,23 @@
     final Root root = Root(apis: <Api>[], classes: <Class>[
       Class(
         name: 'Input',
-        fields: <Field>[Field(name: 'input', dataType: 'String')],
+        fields: <Field>[
+          Field(
+            name: 'input',
+            dataType: 'String',
+            isNullable: true,
+          )
+        ],
       ),
       Class(
         name: 'Nested',
-        fields: <Field>[Field(name: 'nested', dataType: 'Input')],
+        fields: <Field>[
+          Field(
+            name: 'nested',
+            dataType: 'Input',
+            isNullable: true,
+          )
+        ],
       )
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
@@ -110,17 +132,26 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -135,14 +166,19 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'void',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -157,14 +193,19 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'void',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -183,14 +224,19 @@
         Method(
           name: 'doSomething',
           argType: 'void',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -205,14 +251,19 @@
         Method(
           name: 'doSomething',
           argType: 'EnumClass',
+          isArgNullable: false,
           returnType: 'EnumClass',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'EnumClass',
-          fields: <Field>[Field(name: 'enum1', dataType: 'Enum')]),
+      Class(name: 'EnumClass', fields: <Field>[
+        Field(
+          name: 'enum1',
+          dataType: 'Enum',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[
       Enum(
         name: 'Enum',
@@ -237,14 +288,19 @@
         Method(
           name: 'doSomething',
           argType: 'EnumClass',
+          isArgNullable: false,
           returnType: 'EnumClass',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'EnumClass',
-          fields: <Field>[Field(name: 'enum1', dataType: 'Enum')]),
+      Class(name: 'EnumClass', fields: <Field>[
+        Field(
+          name: 'enum1',
+          dataType: 'Enum',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[
       Enum(
         name: 'Enum',
@@ -271,14 +327,19 @@
         Method(
           name: 'doSomething',
           argType: 'void',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -296,23 +357,33 @@
             Method(
               name: 'doSomething',
               argType: 'Input',
+              isArgNullable: false,
               returnType: 'Output',
               isAsynchronous: false,
             ),
             Method(
               name: 'voidReturner',
               argType: 'Input',
+              isArgNullable: false,
               returnType: 'void',
               isAsynchronous: false,
             )
           ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer mainCodeSink = StringBuffer();
     final StringBuffer testCodeSink = StringBuffer();
@@ -342,6 +413,7 @@
         Field(
           name: 'field1',
           dataType: 'dataType1',
+          isNullable: true,
         ),
       ],
     );
@@ -362,17 +434,26 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: true,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -389,17 +470,26 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'void',
           isAsynchronous: true,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -415,17 +505,26 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: true,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
@@ -440,14 +539,19 @@
         Method(
           name: 'doSomething',
           argType: 'void',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: true,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateDart(const DartOptions(isNullSafe: false), root, sink);
diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart
index 94feb1f..9167aa9 100644
--- a/packages/pigeon/test/java_generator_test.dart
+++ b/packages/pigeon/test/java_generator_test.dart
@@ -14,6 +14,7 @@
         Field(
           name: 'field1',
           dataType: 'int',
+          isNullable: true,
         ),
       ],
     );
@@ -63,6 +64,7 @@
         Field(
           name: 'field1',
           dataType: 'int',
+          isNullable: true,
         )
       ],
     );
@@ -86,17 +88,18 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: true,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(name: 'input', dataType: 'String', isNullable: true)
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(name: 'output', dataType: 'String', isNullable: true)
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -110,14 +113,14 @@
   test('all the simple datatypes header', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
       Class(name: 'Foobar', fields: <Field>[
-        Field(name: 'aBool', dataType: 'bool'),
-        Field(name: 'aInt', dataType: 'int'),
-        Field(name: 'aDouble', dataType: 'double'),
-        Field(name: 'aString', dataType: 'String'),
-        Field(name: 'aUint8List', dataType: 'Uint8List'),
-        Field(name: 'aInt32List', dataType: 'Int32List'),
-        Field(name: 'aInt64List', dataType: 'Int64List'),
-        Field(name: 'aFloat64List', dataType: 'Float64List'),
+        Field(name: 'aBool', dataType: 'bool', isNullable: true),
+        Field(name: 'aInt', dataType: 'int', isNullable: true),
+        Field(name: 'aDouble', dataType: 'double', isNullable: true),
+        Field(name: 'aString', dataType: 'String', isNullable: true),
+        Field(name: 'aUint8List', dataType: 'Uint8List', isNullable: true),
+        Field(name: 'aInt32List', dataType: 'Int32List', isNullable: true),
+        Field(name: 'aInt64List', dataType: 'Int64List', isNullable: true),
+        Field(name: 'aFloat64List', dataType: 'Float64List', isNullable: true),
       ]),
     ], enums: <Enum>[]);
 
@@ -141,17 +144,18 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(name: 'input', dataType: 'String', isNullable: true)
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(name: 'output', dataType: 'String', isNullable: true)
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -167,14 +171,15 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'void',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(name: 'input', dataType: 'String', isNullable: true)
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -190,14 +195,15 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'void',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(name: 'input', dataType: 'String', isNullable: true)
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -214,14 +220,15 @@
         Method(
           name: 'doSomething',
           argType: 'void',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(name: 'output', dataType: 'String', isNullable: true)
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -237,14 +244,15 @@
         Method(
           name: 'doSomething',
           argType: 'void',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: false,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(name: 'output', dataType: 'String', isNullable: true)
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -256,9 +264,9 @@
 
   test('gen list', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'List')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(name: 'field1', dataType: 'List', isNullable: true)
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -270,9 +278,9 @@
 
   test('gen map', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'Map')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(name: 'field1', dataType: 'Map', isNullable: true)
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -289,6 +297,7 @@
         Field(
           name: 'nested',
           dataType: 'Nested',
+          isNullable: true,
         )
       ],
     );
@@ -298,6 +307,7 @@
         Field(
           name: 'data',
           dataType: 'int',
+          isNullable: true,
         )
       ],
     );
@@ -324,17 +334,18 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: true,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(name: 'input', dataType: 'String', isNullable: true)
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(name: 'output', dataType: 'String', isNullable: true)
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -357,17 +368,18 @@
         Method(
           name: 'doSomething',
           argType: 'Input',
+          isArgNullable: false,
           returnType: 'Output',
           isAsynchronous: true,
         )
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(name: 'input', dataType: 'String', isNullable: true)
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(name: 'output', dataType: 'String', isNullable: true)
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     const JavaOptions javaOptions = JavaOptions(className: 'Messages');
@@ -391,6 +403,7 @@
         Field(
           name: 'enum1',
           dataType: 'Enum1',
+          isNullable: true,
         ),
       ],
     );
diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart
index f38a788..383e84e 100644
--- a/packages/pigeon/test/objc_generator_test.dart
+++ b/packages/pigeon/test/objc_generator_test.dart
@@ -9,9 +9,13 @@
 void main() {
   test('gen one class header', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'String')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(
+          name: 'field1',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(), root, sink);
@@ -22,9 +26,13 @@
 
   test('gen one class source', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'String')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(
+          name: 'field1',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@@ -76,8 +84,16 @@
         Class(
           name: 'Foobar',
           fields: <Field>[
-            Field(name: 'field1', dataType: 'String'),
-            Field(name: 'enum1', dataType: 'Enum1'),
+            Field(
+              name: 'field1',
+              dataType: 'String',
+              isNullable: true,
+            ),
+            Field(
+              name: 'enum1',
+              dataType: 'Enum1',
+              isNullable: true,
+            ),
           ],
         ),
       ],
@@ -102,15 +118,27 @@
   test('gen one api header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(), root, sink);
@@ -125,15 +153,27 @@
   test('gen one api source', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@@ -147,14 +187,46 @@
   test('all the simple datatypes header', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
       Class(name: 'Foobar', fields: <Field>[
-        Field(name: 'aBool', dataType: 'bool'),
-        Field(name: 'aInt', dataType: 'int'),
-        Field(name: 'aDouble', dataType: 'double'),
-        Field(name: 'aString', dataType: 'String'),
-        Field(name: 'aUint8List', dataType: 'Uint8List'),
-        Field(name: 'aInt32List', dataType: 'Int32List'),
-        Field(name: 'aInt64List', dataType: 'Int64List'),
-        Field(name: 'aFloat64List', dataType: 'Float64List'),
+        Field(
+          name: 'aBool',
+          dataType: 'bool',
+          isNullable: true,
+        ),
+        Field(
+          name: 'aInt',
+          dataType: 'int',
+          isNullable: true,
+        ),
+        Field(
+          name: 'aDouble',
+          dataType: 'double',
+          isNullable: true,
+        ),
+        Field(
+          name: 'aString',
+          dataType: 'String',
+          isNullable: true,
+        ),
+        Field(
+          name: 'aUint8List',
+          dataType: 'Uint8List',
+          isNullable: true,
+        ),
+        Field(
+          name: 'aInt32List',
+          dataType: 'Int32List',
+          isNullable: true,
+        ),
+        Field(
+          name: 'aInt64List',
+          dataType: 'Int64List',
+          isNullable: true,
+        ),
+        Field(
+          name: 'aFloat64List',
+          dataType: 'Float64List',
+          isNullable: true,
+        ),
       ]),
     ], enums: <Enum>[]);
 
@@ -180,7 +252,11 @@
   test('bool source', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
       Class(name: 'Foobar', fields: <Field>[
-        Field(name: 'aBool', dataType: 'bool'),
+        Field(
+          name: 'aBool',
+          dataType: 'bool',
+          isNullable: true,
+        ),
       ]),
     ], enums: <Enum>[]);
 
@@ -193,12 +269,20 @@
 
   test('nested class header', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Nested',
-          fields: <Field>[Field(name: 'nested', dataType: 'Input')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Nested', fields: <Field>[
+        Field(
+          name: 'nested',
+          dataType: 'Input',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(header: 'foo.h'), root, sink);
@@ -209,12 +293,20 @@
 
   test('nested class source', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Nested',
-          fields: <Field>[Field(name: 'nested', dataType: 'Input')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Nested', fields: <Field>[
+        Field(
+          name: 'nested',
+          dataType: 'Input',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@@ -225,9 +317,13 @@
 
   test('prefix class header', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'String')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(
+          name: 'field1',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink);
@@ -237,9 +333,13 @@
 
   test('prefix class source', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'String')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(
+          name: 'field1',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink);
@@ -250,15 +350,27 @@
   test('prefix nested class header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'Nested')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'Nested')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Nested',
-          fields: <Field>[Field(name: 'nested', dataType: 'Input')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Nested', fields: <Field>[
+        Field(
+          name: 'nested',
+          dataType: 'Input',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(prefix: 'ABC'), root, sink);
@@ -271,15 +383,27 @@
   test('prefix nested class source', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'Nested')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'Nested')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Nested',
-          fields: <Field>[Field(name: 'nested', dataType: 'Input')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Nested', fields: <Field>[
+        Field(
+          name: 'nested',
+          dataType: 'Input',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink);
@@ -292,15 +416,27 @@
   test('gen flutter api header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(header: 'foo.h'), root, sink);
@@ -316,15 +452,27 @@
   test('gen flutter api source', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')])
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ])
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(const ObjcOptions(header: 'foo.h'), root, sink);
@@ -336,12 +484,20 @@
   test('gen host void header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'void')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'void')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(
@@ -353,12 +509,20 @@
   test('gen host void source', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'void')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'void')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(
@@ -372,12 +536,20 @@
   test('gen flutter void return header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'void')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'void')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(
@@ -389,12 +561,20 @@
   test('gen flutter void return source', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
-        Method(name: 'doSomething', argType: 'Input', returnType: 'void')
+        Method(
+            name: 'doSomething',
+            argType: 'Input',
+            isArgNullable: false,
+            returnType: 'void')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(
@@ -407,12 +587,20 @@
   test('gen host void arg header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'void', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'void',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(
@@ -424,12 +612,20 @@
   test('gen host void arg source', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
-        Method(name: 'doSomething', argType: 'void', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'void',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(
@@ -441,12 +637,20 @@
   test('gen flutter void arg header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
-        Method(name: 'doSomething', argType: 'void', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'void',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(
@@ -461,12 +665,20 @@
   test('gen flutter void arg header', () {
     final Root root = Root(apis: <Api>[
       Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
-        Method(name: 'doSomething', argType: 'void', returnType: 'Output')
+        Method(
+            name: 'doSomething',
+            argType: 'void',
+            isArgNullable: false,
+            returnType: 'Output')
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(
@@ -481,9 +693,13 @@
 
   test('gen list', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'List')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(
+          name: 'field1',
+          dataType: 'List',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(), root, sink);
@@ -494,9 +710,13 @@
 
   test('gen map', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[
-      Class(
-          name: 'Foobar',
-          fields: <Field>[Field(name: 'field1', dataType: 'Map')]),
+      Class(name: 'Foobar', fields: <Field>[
+        Field(
+          name: 'field1',
+          dataType: 'Map',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(const ObjcOptions(), root, sink);
@@ -511,16 +731,25 @@
         Method(
             name: 'doSomething',
             argType: 'Input',
+            isArgNullable: false,
             returnType: 'void',
             isAsynchronous: true)
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(
@@ -538,16 +767,25 @@
         Method(
             name: 'doSomething',
             argType: 'Input',
+            isArgNullable: false,
             returnType: 'Output',
             isAsynchronous: true)
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(
@@ -565,13 +803,18 @@
         Method(
             name: 'doSomething',
             argType: 'void',
+            isArgNullable: false,
             returnType: 'Output',
             isAsynchronous: true)
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcHeader(
@@ -589,6 +832,7 @@
         Method(
             name: 'doSomething',
             argType: 'void',
+            isArgNullable: false,
             returnType: 'void',
             isAsynchronous: true)
       ])
@@ -609,16 +853,25 @@
         Method(
             name: 'doSomething',
             argType: 'Input',
+            isArgNullable: false,
             returnType: 'Output',
             isAsynchronous: true)
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(
@@ -636,16 +889,25 @@
         Method(
             name: 'doSomething',
             argType: 'Input',
+            isArgNullable: false,
             returnType: 'void',
             isAsynchronous: true)
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Input',
-          fields: <Field>[Field(name: 'input', dataType: 'String')]),
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Input', fields: <Field>[
+        Field(
+          name: 'input',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(
@@ -663,6 +925,7 @@
         Method(
             name: 'doSomething',
             argType: 'void',
+            isArgNullable: false,
             returnType: 'void',
             isAsynchronous: true)
       ])
@@ -681,13 +944,18 @@
         Method(
             name: 'doSomething',
             argType: 'void',
+            isArgNullable: false,
             returnType: 'Output',
             isAsynchronous: true)
       ])
     ], classes: <Class>[
-      Class(
-          name: 'Output',
-          fields: <Field>[Field(name: 'output', dataType: 'String')]),
+      Class(name: 'Output', fields: <Field>[
+        Field(
+          name: 'output',
+          dataType: 'String',
+          isNullable: true,
+        )
+      ]),
     ], enums: <Enum>[]);
     final StringBuffer sink = StringBuffer();
     generateObjcSource(
diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart
index 7273139..7054b3b 100644
--- a/packages/pigeon/test/pigeon_lib_test.dart
+++ b/packages/pigeon/test/pigeon_lib_test.dart
@@ -184,10 +184,12 @@
     expect(input?.fields.length, equals(1));
     expect(input?.fields[0].name, equals('input'));
     expect(input?.fields[0].dataType, equals('String'));
+    expect(input?.fields[0].isNullable, isTrue);
 
     expect(output?.fields.length, equals(1));
     expect(output?.fields[0].name, equals('output'));
     expect(output?.fields[0].dataType, equals('String'));
+    expect(output?.fields[0].isNullable, isTrue);
   });
 
   test('invalid datatype', () {
@@ -208,6 +210,7 @@
     expect(results.root.classes[0].name, equals('ClassWithEnum'));
     expect(results.root.classes[0].fields.length, equals(1));
     expect(results.root.classes[0].fields[0].dataType, equals('Enum1'));
+    expect(results.root.classes[0].fields[0].isNullable, isTrue);
     expect(results.root.classes[0].fields[0].name, equals('enum1'));
   });
 
@@ -232,6 +235,7 @@
         results.root.classes.firstWhere((Class x) => x.name == 'Nested');
     expect(nested.fields.length, equals(1));
     expect(nested.fields[0].dataType, equals('Input1'));
+    expect(nested.fields[0].isNullable, isTrue);
   });
 
   test('flutter api', () {
@@ -417,6 +421,161 @@
     });
   });
 
+  test('test method in data class error', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+class Foo {
+  int? x;
+  int? foo() { return x; }
+}
+
+@HostApi()
+abstract class Api {
+  Foo doit(Foo foo);
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 3);
+      expect(results.errors[0].message, contains('Method'));
+    });
+  });
+
+  test('test field initialization', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+class Foo {
+  int? x = 123;  
+}
+
+@HostApi()
+abstract class Api {
+  Foo doit(Foo foo);
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 2);
+      expect(results.errors[0].message, contains('Initialization'));
+    });
+  });
+
+  test('test field in api error', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+class Foo {
+  int? x;
+}
+
+@HostApi()
+abstract class Api {
+  int? x;
+  Foo doit(Foo foo);
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 7);
+      expect(results.errors[0].message, contains('Field'));
+    });
+  });
+
+  test('constructor in data class', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+class Foo {
+  int? x;
+  Foo(this.x);
+}
+
+@HostApi()
+abstract class Api {
+  Foo doit(Foo foo);
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 3);
+      expect(results.errors[0].message, contains('Constructor'));
+    });
+  });
+
+  test('nullable api arguments', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+class Foo {
+  int? x;
+}
+
+@HostApi()
+abstract class Api {
+  Foo doit(Foo? foo);
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 7);
+      expect(results.errors[0].message, contains('Nullable'));
+    });
+  });
+
+  test('nullable api return', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+class Foo {
+  int? x;
+}
+
+@HostApi()
+abstract class Api {
+  Foo? doit(Foo foo);
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 7);
+      expect(results.errors[0].message, contains('Nullable'));
+    });
+  });
+
+  test('primitive arguments', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+@HostApi()
+abstract class Api {
+  void doit(int foo);
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 3);
+      expect(results.errors[0].message, contains('Primitive'));
+    });
+  });
+
+  test('primitive return', () {
+    final Pigeon dartle = Pigeon.setup();
+    _withTempFile('compilationError.dart', (File file) {
+      file.writeAsStringSync('''
+@HostApi()
+abstract class Api {
+  int doit();
+}
+''');
+      final ParseResults results = dartle.parseFile(file.path);
+      expect(results.errors.length, 1);
+      expect(results.errors[0].lineNumber, 3);
+      expect(results.errors[0].message, contains('Primitive'));
+    });
+  });
+
   test('test invalid import', () {
     final Pigeon dartle = Pigeon.setup();
     _withTempFile('compilationError.dart', (File file) {