MockBuilder: Include type arguments on dummy return values.

Without type arguments, runtime errors can take place. A stub method may try to use `[]` as a return value for type `<int>[]`, but inference does not occur, so `[]` is implicitly `<dynamic>[]`.

Additionally, include type arguments on function types.

Clean up tests which are affected by these two fixes.

PiperOrigin-RevId: 316966473
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index 7c82bdf..030d510 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -356,106 +356,132 @@
   }
 
   Expression _dummyValue(analyzer.DartType type) {
-    if (type.isDartCoreBool) {
+    if (type is analyzer.FunctionType) {
+      return _dummyFunctionValue(type);
+    }
+
+    if (type is! analyzer.InterfaceType) {
+      // TODO(srawlins): This case is not known.
+      return literalNull;
+    }
+
+    var interfaceType = type as analyzer.InterfaceType;
+    var typeArguments = interfaceType.typeArguments;
+    if (interfaceType.isDartCoreBool) {
       return literalFalse;
-    } else if (type.isDartCoreDouble) {
+    } else if (interfaceType.isDartCoreDouble) {
       return literalNum(0.0);
-    } else if (type.isDartAsyncFuture || type.isDartAsyncFutureOr) {
-      var typeArgument = (type as analyzer.InterfaceType).typeArguments.first;
+    } else if (interfaceType.isDartAsyncFuture ||
+        interfaceType.isDartAsyncFutureOr) {
+      var typeArgument = typeArguments.first;
       return refer('Future')
           .property('value')
           .call([_dummyValue(typeArgument)]);
-    } else if (type.isDartCoreInt) {
+    } else if (interfaceType.isDartCoreInt) {
       return literalNum(0);
-    } else if (type.isDartCoreIterable) {
+    } else if (interfaceType.isDartCoreIterable) {
       return literalList([]);
-    } else if (type.isDartCoreList) {
-      return literalList([]);
-    } else if (type.isDartCoreMap) {
-      return literalMap({});
-    } else if (type.isDartCoreNum) {
+    } else if (interfaceType.isDartCoreList) {
+      assert(typeArguments.length == 1);
+      var elementType = _typeReference(typeArguments[0]);
+      return literalList([], elementType);
+    } else if (interfaceType.isDartCoreMap) {
+      assert(typeArguments.length == 2);
+      var keyType = _typeReference(typeArguments[0]);
+      var valueType = _typeReference(typeArguments[1]);
+      return literalMap({}, keyType, valueType);
+    } else if (interfaceType.isDartCoreNum) {
       return literalNum(0);
-    } else if (type.isDartCoreSet) {
-      // This is perhaps a dangerous hack. The code, `{}`, is parsed as a Set
-      // literal if it is used in a context which explicitly expects a Set.
-      return literalMap({});
-    } else if (type.element?.declaration == typeProvider.streamElement) {
-      return refer('Stream').property('empty').call([]);
-    } else if (type.isDartCoreString) {
+    } else if (interfaceType.isDartCoreSet) {
+      assert(typeArguments.length == 1);
+      var elementType = _typeReference(typeArguments[0]);
+      return literalSet({}, elementType);
+    } else if (interfaceType.element?.declaration ==
+        typeProvider.streamElement) {
+      assert(typeArguments.length == 1);
+      var elementType = _typeReference(typeArguments[0]);
+      return TypeReference((b) {
+        b
+          ..symbol = 'Stream'
+          ..types.add(elementType);
+      }).property('empty').call([]);
+    } else if (interfaceType.isDartCoreString) {
       return literalString('');
-    } else {
-      // This class is unknown; we must likely generate a fake class, and return
-      // an instance here.
-      return _dummyValueImplementing(type);
     }
+
+    // This class is unknown; we must likely generate a fake class, and return
+    // an instance here.
+    return _dummyValueImplementing(type);
   }
 
-  Expression _dummyValueImplementing(analyzer.DartType dartType) {
-    // For each type parameter on [classToMock], the Mock class needs a type
-    // parameter with same type variables, and a mirrored type argument for
-    // the "implements" clause.
-    var typeArguments = <Reference>[];
-    var elementToFake = dartType.element;
-    if (elementToFake is ClassElement) {
-      if (elementToFake.isEnum) {
-        return _typeReference(dartType).property(
-            elementToFake.fields.firstWhere((f) => f.isEnumConstant).name);
-      } else {
-        // There is a potential for these names to collide. If one mock class
-        // requires a fake for a certain Foo, and another mock class requires a
-        // fake for a different Foo, they will collide.
-        var fakeName = '_Fake${dartType.name}';
-        // Only make one fake class for each class that needs to be faked.
-        if (!fakedClassElements.contains(elementToFake)) {
-          fakeClasses.add(Class((cBuilder) {
-            cBuilder
-              ..name = fakeName
-              ..extend = refer('Fake', 'package:mockito/mockito.dart');
-            if (elementToFake.typeParameters != null) {
-              for (var typeParameter in elementToFake.typeParameters) {
-                cBuilder.types.add(_typeParameterReference(typeParameter));
-                typeArguments.add(refer(typeParameter.name));
-              }
-            }
-            cBuilder.implements.add(TypeReference((b) {
-              b
-                ..symbol = dartType.name
-                ..url = _typeImport(dartType)
-                ..types.addAll(typeArguments);
-            }));
-          }));
-          fakedClassElements.add(elementToFake);
+  Expression _dummyFunctionValue(analyzer.FunctionType type) {
+    return Method((b) {
+      // The positional parameters in a FunctionType have no names. This
+      // counter lets us create unique dummy names.
+      var counter = 0;
+      for (final parameter in type.parameters) {
+        if (parameter.isRequiredPositional) {
+          b.requiredParameters
+              .add(_matchingParameter(parameter, defaultName: '__p$counter'));
+          counter++;
+        } else if (parameter.isOptionalPositional) {
+          b.optionalParameters
+              .add(_matchingParameter(parameter, defaultName: '__p$counter'));
+          counter++;
+        } else if (parameter.isNamed) {
+          b.optionalParameters.add(_matchingParameter(parameter));
         }
-        return refer(fakeName).newInstance([]);
       }
-    } else if (dartType is analyzer.FunctionType) {
-      return Method((b) {
-        // The positional parameters in a FunctionType have no names. This
-        // counter lets us create unique dummy names.
-        var counter = 0;
-        for (final parameter in dartType.parameters) {
-          if (parameter.isRequiredPositional) {
-            b.requiredParameters
-                .add(_matchingParameter(parameter, defaultName: '__p$counter'));
-            counter++;
-          } else if (parameter.isOptionalPositional) {
-            b.optionalParameters
-                .add(_matchingParameter(parameter, defaultName: '__p$counter'));
-            counter++;
-          } else if (parameter.isNamed) {
-            b.optionalParameters.add(_matchingParameter(parameter));
-          }
-        }
-        if (dartType.returnType.isVoid) {
-          b.body = Code('');
-        } else {
-          b.body = _dummyValue(dartType.returnType).code;
-        }
-      }).closure;
-    }
+      if (type.returnType.isVoid) {
+        b.body = Code('');
+      } else {
+        b.body = _dummyValue(type.returnType).code;
+      }
+    }).closure;
+  }
 
-    // We shouldn't get here.
-    return literalNull;
+  Expression _dummyValueImplementing(analyzer.InterfaceType dartType) {
+    // For each type parameter on [dartType], the Mock class needs a type
+    // parameter with same type variables, and a mirrored type argument for the
+    // "implements" clause.
+    var typeParameters = <Reference>[];
+    var elementToFake = dartType.element;
+    if (elementToFake.isEnum) {
+      return _typeReference(dartType).property(
+          elementToFake.fields.firstWhere((f) => f.isEnumConstant).name);
+    } else {
+      // There is a potential for these names to collide. If one mock class
+      // requires a fake for a certain Foo, and another mock class requires a
+      // fake for a different Foo, they will collide.
+      var fakeName = '_Fake${dartType.name}';
+      // Only make one fake class for each class that needs to be faked.
+      if (!fakedClassElements.contains(elementToFake)) {
+        fakeClasses.add(Class((cBuilder) {
+          cBuilder
+            ..name = fakeName
+            ..extend = refer('Fake', 'package:mockito/mockito.dart');
+          if (elementToFake.typeParameters != null) {
+            for (var typeParameter in elementToFake.typeParameters) {
+              cBuilder.types.add(_typeParameterReference(typeParameter));
+              typeParameters.add(refer(typeParameter.name));
+            }
+          }
+          cBuilder.implements.add(TypeReference((b) {
+            b
+              ..symbol = dartType.name
+              ..url = _typeImport(dartType)
+              ..types.addAll(typeParameters);
+          }));
+        }));
+        fakedClassElements.add(elementToFake);
+      }
+      var typeArguments = dartType.typeArguments;
+      return TypeReference((b) {
+        b
+          ..symbol = fakeName
+          ..types.addAll(typeArguments.map(_typeReference));
+      }).newInstance([]);
+    }
   }
 
   /// Returns a [Parameter] which matches [parameter].
@@ -582,6 +608,9 @@
           for (var parameter in type.namedParameterTypes.entries) {
             b.namedParameters[parameter.key] = _typeReference(parameter.value);
           }
+          if (type.typeFormals != null) {
+            b.types.addAll(type.typeFormals.map(_typeParameterReference));
+          }
         });
       }
       return TypeReference((b) {
diff --git a/test/builder_test.dart b/test/builder_test.dart
index ba0e3b9..578bb45 100644
--- a/test/builder_test.dart
+++ b/test/builder_test.dart
@@ -145,7 +145,7 @@
       }
       '''),
       _containsAllOf('_i3.Stream<int> m() =>',
-          'super.noSuchMethod(Invocation.method(#m, []), Stream.empty());'),
+          'super.noSuchMethod(Invocation.method(#m, []), Stream<int>.empty());'),
     );
   });
 
@@ -534,40 +534,63 @@
     );
   });
 
-  test('matches nullability of return types', () async {
-    await _testWithNonNullable(
-      {
-        ...annotationsAsset,
-        ...simpleTestAsset,
-        'foo|lib/foo.dart': dedent(r'''
+  test('matches nullability of non-nullable return type', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
         abstract class Foo {
-          int f(int a);
-          int? g(int a);
-          List<int?> h(int a);
-          List<int> i(int a);
-          j(int a);
-          T? k<T extends int>(int a);
+          int m(int a);
         }
         '''),
-      },
-      outputs: {
-        'foo|test/foo_test.mocks.dart': dedent(r'''
-        import 'package:mockito/mockito.dart' as _i1;
-        import 'package:foo/foo.dart' as _i2;
+      _containsAllOf(
+          'int m(int? a) => super.noSuchMethod(Invocation.method(#m, [a]), 0);'),
+    );
+  });
 
-        /// A class which mocks [Foo].
-        ///
-        /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends _i1.Mock implements _i2.Foo {
-          int f(int? a) => super.noSuchMethod(Invocation.method(#f, [a]), 0);
-          int? g(int? a) => super.noSuchMethod(Invocation.method(#g, [a]));
-          List<int?> h(int? a) => super.noSuchMethod(Invocation.method(#h, [a]), []);
-          List<int> i(int? a) => super.noSuchMethod(Invocation.method(#i, [a]), []);
-          dynamic j(int? a) => super.noSuchMethod(Invocation.method(#j, [a]));
-          T? k<T extends int>(int? a) => super.noSuchMethod(Invocation.method(#k, [a]));
+  test('matches nullability of nullable return type', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          int? m(int a);
         }
         '''),
-      },
+      _containsAllOf(
+          'int? m(int? a) => super.noSuchMethod(Invocation.method(#m, [a]));'),
+    );
+  });
+
+  test('matches nullability of return type type arguments', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          List<int?> m(int a);
+        }
+        '''),
+      _containsAllOf('List<int?> m(int? a) =>',
+          'super.noSuchMethod(Invocation.method(#m, [a]), <int?>[]);'),
+    );
+  });
+
+  test('matches nullability of nullable type variable return type', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          T? m<T>(int a);
+        }
+        '''),
+      _containsAllOf(
+          'T? m<T>(int? a) => super.noSuchMethod(Invocation.method(#m, [a]));'),
+    );
+  });
+
+  test('overrides implicit return type with dynamic', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+        abstract class Foo {
+          m(int a);
+        }
+        '''),
+      _containsAllOf(
+          'dynamic m(int? a) => super.noSuchMethod(Invocation.method(#m, [a]));'),
     );
   });
 
@@ -923,8 +946,8 @@
         List<Foo> m() => [Foo()];
       }
       '''),
-      _containsAllOf(
-          'List<_i2.Foo> m() => super.noSuchMethod(Invocation.method(#m, []), []);'),
+      _containsAllOf('List<_i2.Foo> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), <_i2.Foo>[]);'),
     );
   });
 
@@ -935,8 +958,8 @@
         Set<Foo> m() => {Foo()};
       }
       '''),
-      _containsAllOf(
-          'Set<_i2.Foo> m() => super.noSuchMethod(Invocation.method(#m, []), {});'),
+      _containsAllOf('Set<_i2.Foo> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), <_i2.Foo>{});'),
     );
   });
 
@@ -947,8 +970,20 @@
         Map<int, Foo> m() => {7: Foo()};
       }
       '''),
-      _containsAllOf(
-          'Map<int, _i2.Foo> m() => super.noSuchMethod(Invocation.method(#m, []), {});'),
+      _containsAllOf('Map<int, _i2.Foo> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), <int, _i2.Foo>{});'),
+    );
+  });
+
+  test('creates dummy non-null raw-typed return value', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      abstract class Foo {
+        Map m();
+      }
+      '''),
+      _containsAllOf('Map<dynamic, dynamic> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), <dynamic, dynamic>{});'),
     );
   });
 
@@ -965,6 +1000,18 @@
     );
   });
 
+  test('creates dummy non-null Stream return value', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      abstract class Foo {
+        Stream<int> m();
+      }
+      '''),
+      _containsAllOf('Stream<int> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), Stream<int>.empty());'),
+    );
+  });
+
   test('creates dummy non-null return values for unknown classes', () async {
     await _expectSingleNonNullableOutput(
       dedent(r'''
@@ -981,6 +1028,19 @@
     );
   });
 
+  test('creates dummy non-null return values for generic type', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      abstract class Foo {
+        Bar<int> m();
+      }
+      class Bar<T> {}
+      '''),
+      _containsAllOf('Bar<int> m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), _FakeBar<int>());'),
+    );
+  });
+
   test('creates dummy non-null return values for enums', () async {
     await _expectSingleNonNullableOutput(
       dedent(r'''
@@ -998,7 +1058,7 @@
   });
 
   test(
-      'creates a dummy non-null return function-typed value, with optional '
+      'creates a dummy non-null function-typed return value, with optional '
       'parameters', () async {
     await _expectSingleNonNullableOutput(
       dedent(r'''
@@ -1012,7 +1072,7 @@
   });
 
   test(
-      'creates a dummy non-null return function-typed value, with named '
+      'creates a dummy non-null function-typed return value, with named '
       'parameters', () async {
     await _expectSingleNonNullableOutput(
       dedent(r'''
@@ -1026,7 +1086,7 @@
   });
 
   test(
-      'creates a dummy non-null return function-typed value, with non-core '
+      'creates a dummy non-null function-typed return value, with non-core '
       'return type', () async {
     await _expectSingleNonNullableOutput(
       dedent(r'''
@@ -1039,6 +1099,44 @@
     );
   });
 
+  test('creates a dummy non-null generic function-typed return value',
+      () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        T Function<T>(T) m() => (int i, [String s]) {};
+      }
+      '''),
+      _containsAllOf('T Function<T>(T) m() =>',
+          'super.noSuchMethod(Invocation.method(#m, []), (T __p0) => null);'),
+    );
+  });
+
+  test('generates a fake class used in return values', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        Bar m1() => Bar('name1');
+      }
+      class Bar {}
+      '''),
+      _containsAllOf('class _FakeBar extends _i1.Fake implements _i2.Bar {}'),
+    );
+  });
+
+  test('generates a fake generic class used in return values', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        Bar m1() => Bar('name1');
+      }
+      class Bar<T, U> {}
+      '''),
+      _containsAllOf(
+          'class _FakeBar<T, U> extends _i1.Fake implements _i2.Bar<T, U> {}'),
+    );
+  });
+
   test('deduplicates fake classes', () async {
     await _expectSingleNonNullableOutput(
       dedent(r'''