[pigeon] fixed cases where types can be missing from codecs (#468)

diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 86b2a97..da16220 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 1.0.4
+
+* [front-end] Fixed bug where codecs weren't generating support for types that
+  only show up in type arguments.
+
 ## 1.0.3
 
 * [objc] Updated assert message for incomplete implementations of protocols.
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 51df982..451fcc2 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 = '1.0.3';
+const String pigeonVersion = '1.0.4';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
@@ -280,14 +280,21 @@
 /// avoid collisions with the StandardMessageCodec.
 const int _minimumCodecFieldKey = 128;
 
+Iterable<String> _getReferencedTypes(TypeDeclaration type) sync* {
+  for (final TypeDeclaration typeArg in type.typeArguments) {
+    yield* _getReferencedTypes(typeArg);
+  }
+  yield type.baseName;
+}
+
 /// Given an [Api], return the enumerated classes that must exist in the codec
 /// where the enumeration should be the key used in the buffer.
 Iterable<EnumeratedClass> getCodecClasses(Api api) sync* {
   final Set<String> names = <String>{};
   for (final Method method in api.methods) {
-    names.add(method.returnType.baseName);
-    if (method.arguments.isNotEmpty) {
-      names.add(method.arguments[0].type.baseName);
+    names.addAll(_getReferencedTypes(method.returnType));
+    for (final NamedType argument in method.arguments) {
+      names.addAll(_getReferencedTypes(argument.type));
     }
   }
   final List<String> sortedNames = names
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index 5f898b4..ca87553 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/master/packages/pigeon
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 1.0.3 # This must match the version in lib/generator_tools.dart
+version: 1.0.4 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: '>=2.12.0 <3.0.0'
diff --git a/packages/pigeon/test/generator_tools_test.dart b/packages/pigeon/test/generator_tools_test.dart
index 034d94d..ea6bb90 100644
--- a/packages/pigeon/test/generator_tools_test.dart
+++ b/packages/pigeon/test/generator_tools_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:pigeon/ast.dart';
 import 'package:pigeon/generator_tools.dart';
 import 'package:test/test.dart';
 
@@ -63,4 +64,115 @@
     };
     expect(_equalMaps(expected, mergeMaps(source, modification)), isTrue);
   });
+
+  test('get codec classes from argument type arguments', () {
+    final Api api =
+        Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+      Method(
+        name: 'doSomething',
+        arguments: <NamedType>[
+          NamedType(
+            type: TypeDeclaration(
+              baseName: 'List',
+              isNullable: false,
+              typeArguments: <TypeDeclaration>[
+                TypeDeclaration(baseName: 'Input', isNullable: true)
+              ],
+            ),
+            name: '',
+            offset: null,
+          )
+        ],
+        returnType: TypeDeclaration(baseName: 'Output', isNullable: false),
+        isAsynchronous: true,
+      )
+    ]);
+    final List<EnumeratedClass> classes = getCodecClasses(api).toList();
+    expect(classes.length, 2);
+    expect(
+        classes
+            .where((EnumeratedClass element) => element.name == 'Input')
+            .length,
+        1);
+    expect(
+        classes
+            .where((EnumeratedClass element) => element.name == 'Output')
+            .length,
+        1);
+  });
+
+  test('get codec classes from return value type arguments', () {
+    final Api api =
+        Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+      Method(
+        name: 'doSomething',
+        arguments: <NamedType>[
+          NamedType(
+            type: TypeDeclaration(baseName: 'Output', isNullable: false),
+            name: '',
+            offset: null,
+          )
+        ],
+        returnType: TypeDeclaration(
+          baseName: 'List',
+          isNullable: false,
+          typeArguments: <TypeDeclaration>[
+            TypeDeclaration(baseName: 'Input', isNullable: true)
+          ],
+        ),
+        isAsynchronous: true,
+      )
+    ]);
+    final List<EnumeratedClass> classes = getCodecClasses(api).toList();
+    expect(classes.length, 2);
+    expect(
+        classes
+            .where((EnumeratedClass element) => element.name == 'Input')
+            .length,
+        1);
+    expect(
+        classes
+            .where((EnumeratedClass element) => element.name == 'Output')
+            .length,
+        1);
+  });
+
+  test('get codec classes from all arguments', () {
+    final Api api =
+        Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+      Method(
+        name: 'doSomething',
+        arguments: <NamedType>[
+          NamedType(
+            type: TypeDeclaration(baseName: 'Foo', isNullable: false),
+            name: '',
+            offset: null,
+          ),
+          NamedType(
+            type: TypeDeclaration(baseName: 'Bar', isNullable: false),
+            name: '',
+            offset: null,
+          ),
+        ],
+        returnType: TypeDeclaration(
+          baseName: 'List',
+          isNullable: false,
+          typeArguments: <TypeDeclaration>[TypeDeclaration.voidDeclaration()],
+        ),
+        isAsynchronous: true,
+      )
+    ]);
+    final List<EnumeratedClass> classes = getCodecClasses(api).toList();
+    expect(classes.length, 2);
+    expect(
+        classes
+            .where((EnumeratedClass element) => element.name == 'Foo')
+            .length,
+        1);
+    expect(
+        classes
+            .where((EnumeratedClass element) => element.name == 'Bar')
+            .length,
+        1);
+  });
 }