MockBuilder: Override getters; correct bugs in overriding setters.

Getter support is added here, and only public non-nullable instance getters are overridden.

Setter support is improved; previously all public instance setters were overridden. This change ensures that only non-nullable setters are overridden.

Small tests are added for fields, final fields, getters, and setters.

PiperOrigin-RevId: 315014983
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index 99a2a3f..c637462 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -176,9 +176,13 @@
         if (field.isPrivate || field.isStatic) {
           continue;
         }
-        // Handle getters when we handle non-nullable return types.
+        final getter = field.getter;
+        if (getter != null && _returnTypeIsNonNullable(getter)) {
+          cBuilder.methods.add(
+              Method((mBuilder) => _buildOverridingGetter(mBuilder, getter)));
+        }
         final setter = field.setter;
-        if (setter != null) {
+        if (setter != null && _hasNonNullableParameter(setter)) {
           cBuilder.methods.add(
               Method((mBuilder) => _buildOverridingSetter(mBuilder, setter)));
         }
@@ -196,7 +200,7 @@
     });
   }
 
-  bool _returnTypeIsNonNullable(MethodElement method) =>
+  bool _returnTypeIsNonNullable(ExecutableElement method) =>
       typeSystem.isPotentiallyNonNullable(method.returnType);
 
   // Returns whether [method] has at least one parameter whose type is
@@ -210,7 +214,7 @@
   //     }
   //     final c1 = C<int?>(); // m's parameter's type is nullable.
   //     final c2 = C<int>(); // m's parameter's type is non-nullable.
-  bool _hasNonNullableParameter(MethodElement method) =>
+  bool _hasNonNullableParameter(ExecutableElement method) =>
       method.parameters.any((p) => typeSystem.isPotentiallyNonNullable(p.type));
 
   /// Build a method which overrides [method], with all non-nullable
@@ -222,8 +226,6 @@
   // tests for typedefs, old-style function parameters, function types, type
   // variables, non-nullable type variables (bounded to Object, I think),
   // dynamic.
-  // TODO(srawlins): This method declares no specific non-null return values
-  // yet.
   void _buildOverridingMethod(MethodBuilder builder, MethodElement method) {
     // TODO(srawlins): generator methods like async*, sync*.
     var name = method.displayName;
@@ -261,10 +263,7 @@
             refer(parameter.displayName);
       }
     }
-    // TODO(srawlins): Optionally pass a non-null return value to `noSuchMethod`
-    // which `Mock.noSuchMethod` will simply return, in order to satisfy runtime
-    // type checks.
-    // TODO(srawlins): Handle getter invocations with `Invocation.getter`.
+
     final invocation = refer('Invocation').property('method').call([
       refer('#${method.displayName}'),
       literalList(invocationPositionalArgs),
@@ -392,6 +391,27 @@
     });
   }
 
+  /// Build a getter which overrides [getter].
+  ///
+  /// This new method just calls `super.noSuchMethod`, optionally passing a
+  /// return value for non-nullable getters.
+  void _buildOverridingGetter(
+      MethodBuilder builder, PropertyAccessorElement getter) {
+    builder
+      ..name = getter.displayName
+      ..type = MethodType.getter
+      ..returns = _typeReference(getter.returnType);
+
+    final invocation = refer('Invocation').property('getter').call([
+      refer('#${getter.displayName}'),
+    ]);
+    final noSuchMethodArgs = [invocation, _dummyValue(getter.returnType)];
+    final returnNoSuchMethod =
+        refer('super').property('noSuchMethod').call(noSuchMethodArgs);
+
+    builder.body = returnNoSuchMethod.code;
+  }
+
   /// Build a setter which overrides [setter], widening the single parameter
   /// type to be nullable if it is non-nullable.
   ///
diff --git a/test/builder_test.dart b/test/builder_test.dart
index 37c6188..fb550e8 100644
--- a/test/builder_test.dart
+++ b/test/builder_test.dart
@@ -73,34 +73,6 @@
   });
 
   test(
-      'generates mock for an imported class but does not override private '
-      'or static fields', () async {
-    await _testWithNonNullable(
-      {
-        ...annotationsAsset,
-        ...simpleTestAsset,
-        'foo|lib/foo.dart': dedent(r'''
-        class Foo {
-          int _a;
-          static int b;
-        }
-        '''),
-      },
-      outputs: {
-        'foo|test/foo_test.mocks.dart': dedent(r'''
-        import 'package:mockito/mockito.dart' as _i1;
-        import 'package:foo/foo.dart' as _i2;
-
-        /// A class which mocks [Foo].
-        ///
-        /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends _i1.Mock implements _i2.Foo {}
-        '''),
-      },
-    );
-  });
-
-  test(
       'generates mock for an imported class but does not override any '
       'extension methods', () async {
     await _testWithNonNullable(
@@ -664,34 +636,108 @@
     );
   });
 
-  test('overrides getters and setters', () async {
-    await _testWithNonNullable(
-      {
-        ...annotationsAsset,
-        ...simpleTestAsset,
-        'foo|lib/foo.dart': dedent(r'''
-        class Foo {
-          int get a => n + 1;
-          int _b;
-          set b(int value) => _b = value;
-        }
-        '''),
-      },
-      outputs: {
-        // TODO(srawlins): The getter will appear when it has a non-nullable
-        // return type.
-        'foo|test/foo_test.mocks.dart': dedent(r'''
-        import 'package:mockito/mockito.dart' as _i1;
-        import 'package:foo/foo.dart' as _i2;
+  test('overrides non-nullable instance getters', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        int get m => 7;
+      }
+      '''),
+      _containsAllOf(
+          'int get m => super.noSuchMethod(Invocation.getter(#m), 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 {
-          set b(int value) => super.noSuchMethod(Invocation.setter(#b, [value]));
-        }
-        '''),
-      },
+  test('does not override nullable instance getters', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        int? get m => 7;
+      }
+      '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
+
+  test('overrides non-nullable instance setters', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        void set m(int a) {}
+      }
+      '''),
+      _containsAllOf(
+          'set m(int a) => super.noSuchMethod(Invocation.setter(#m, [a]));'),
+    );
+  });
+
+  test('does not override nullable instance setters', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        void set m(int? a) {}
+      }
+      '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
+
+  test('overrides non-nullable fields', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        int m;
+      }
+      '''),
+      _containsAllOf(
+          'int get m => super.noSuchMethod(Invocation.getter(#m), 0);',
+          'set m(int _m) => super.noSuchMethod(Invocation.setter(#m, [_m]));'),
+    );
+  });
+
+  test('overrides final non-nullable fields', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        final int m;
+        Foo(this.m);
+      }
+      '''),
+      _containsAllOf(
+          'int get m => super.noSuchMethod(Invocation.getter(#m), 0);'),
+    );
+  });
+
+  test('does not override nullable fields', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        int? m;
+      }
+      '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
+
+  test('does not override private fields', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        int _a;
+      }
+      '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+    );
+  });
+
+  test('does not override static fields', () async {
+    await _expectSingleNonNullableOutput(
+      dedent(r'''
+      class Foo {
+        static int b;
+      }
+      '''),
+      _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
     );
   });