MockBuilder: refactor much of the class-gathering code into a class.

The class is not terribly complicated. It contains the entryLib which was previously a parameter to almost every other MockBuilder method. This shrinks the build method, and isolates the task of gathering class names from @GenerateMocks annotations.

This will also make the next step much easier: named mocks with possible type parameters in GenerateMock annotations, like `@GenerateMock(Of<Foo<String>>(), as: #MockFooOfString)`.

PiperOrigin-RevId: 318290959
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index 412a512..ebf98b7 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -38,11 +38,58 @@
 /// 'foo.mocks.dart' will be created.
 class MockBuilder implements Builder {
   @override
-  Future build(BuildStep buildStep) async {
+  Future<void> build(BuildStep buildStep) async {
     final entryLib = await buildStep.inputLibrary;
     if (entryLib == null) return;
     final sourceLibIsNonNullable = entryLib.isNonNullableByDefault;
     final mockLibraryAsset = buildStep.inputId.changeExtension('.mocks.dart');
+    final mockTargetGatherer = _MockTargetGatherer(entryLib);
+
+    final mockLibrary = Library((b) {
+      var mockLibraryInfo = _MockLibraryInfo(mockTargetGatherer._classesToMock,
+          sourceLibIsNonNullable: sourceLibIsNonNullable,
+          typeProvider: entryLib.typeProvider,
+          typeSystem: entryLib.typeSystem);
+      b.body.addAll(mockLibraryInfo.fakeClasses);
+      b.body.addAll(mockLibraryInfo.mockClasses);
+    });
+
+    if (mockLibrary.body.isEmpty) {
+      // Nothing to mock here!
+      return;
+    }
+
+    final emitter =
+        DartEmitter.scoped(useNullSafetySyntax: sourceLibIsNonNullable);
+    final mockLibraryContent =
+        DartFormatter().format(mockLibrary.accept(emitter).toString());
+
+    await buildStep.writeAsString(mockLibraryAsset, mockLibraryContent);
+  }
+
+  @override
+  final buildExtensions = const {
+    '.dart': ['.mocks.dart']
+  };
+}
+
+/// This class gathers and verifies mock targets referenced in `GenerateMocks`
+/// annotations.
+// TODO(srawlins): This also needs to gather mock targets (with overridden
+// names, type arguments, etc.) found in `GenerateMock` annotations.
+class _MockTargetGatherer {
+  final LibraryElement _entryLib;
+
+  final List<analyzer.DartType> _classesToMock;
+
+  _MockTargetGatherer._(this._entryLib, this._classesToMock) {
+    _checkClassesToMockAreValid();
+  }
+
+  /// Searches the top-level elements of [entryLib] for `GenerateMocks`
+  /// annotations and creates a [_MockTargetGatherer] with all of the classes
+  /// identified as mocking targets.
+  factory _MockTargetGatherer(LibraryElement entryLib) {
     final objectsToMock = <DartObject>{};
 
     for (final element in entryLib.topLevelElements) {
@@ -70,28 +117,7 @@
     var classesToMock =
         _mapAnnotationValuesToClasses(objectsToMock, entryLib.typeProvider);
 
-    _checkClassesToMockAreValid(classesToMock, entryLib);
-
-    final mockLibrary = Library((b) {
-      var mockLibraryInfo = _MockLibraryInfo(classesToMock,
-          sourceLibIsNonNullable: sourceLibIsNonNullable,
-          typeProvider: entryLib.typeProvider,
-          typeSystem: entryLib.typeSystem);
-      b.body.addAll(mockLibraryInfo.fakeClasses);
-      b.body.addAll(mockLibraryInfo.mockClasses);
-    });
-
-    if (mockLibrary.body.isEmpty) {
-      // Nothing to mock here!
-      return;
-    }
-
-    final emitter =
-        DartEmitter.scoped(useNullSafetySyntax: sourceLibIsNonNullable);
-    final mockLibraryContent =
-        DartFormatter().format(mockLibrary.accept(emitter).toString());
-
-    await buildStep.writeAsString(mockLibraryAsset, mockLibraryContent);
+    return _MockTargetGatherer._(entryLib, classesToMock);
   }
 
   /// Map the values passed to the GenerateMocks annotation to the classes which
@@ -100,7 +126,7 @@
   /// This function is responsible for ensuring that each value is an
   /// appropriate target for mocking. It will throw an
   /// [InvalidMockitoAnnotationException] under various conditions.
-  List<analyzer.DartType> _mapAnnotationValuesToClasses(
+  static List<analyzer.DartType> _mapAnnotationValuesToClasses(
       Iterable<DartObject> objectsToMock, TypeProvider typeProvider) {
     var classesToMock = <analyzer.DartType>[];
 
@@ -144,11 +170,11 @@
     return classesToMock;
   }
 
-  void _checkClassesToMockAreValid(
-      List<analyzer.DartType> classesToMock, LibraryElement entryLib) {
-    var classesInEntryLib = entryLib.topLevelElements.whereType<ClassElement>();
+  void _checkClassesToMockAreValid() {
+    var classesInEntryLib =
+        _entryLib.topLevelElements.whereType<ClassElement>();
     var classNamesToMock = <String, ClassElement>{};
-    for (var class_ in classesToMock) {
+    for (var class_ in _classesToMock) {
       var name = class_.element.name;
       if (classNamesToMock.containsKey(name)) {
         var firstSource = classNamesToMock[name].source.fullName;
@@ -186,7 +212,7 @@
             'appears to already be mocked inline: ${preexistingMock.name}');
       }
 
-      _checkMethodsToStubAreValid(element, entryLib);
+      _checkMethodsToStubAreValid(element);
     });
   }
 
@@ -198,18 +224,11 @@
   ///   signature of such a method.
   /// - It has a non-nullable type variable return type, for example `T m<T>()`.
   ///   Mockito cannot generate dummy return values for unknown types.
-  void _checkMethodsToStubAreValid(
-      ClassElement classElement, LibraryElement entryLib) {
+  void _checkMethodsToStubAreValid(ClassElement classElement) {
     var className = classElement.name;
-    //var unstubbableErrorMessages = <String>[];
-
-    /*for (var method in classElement.methods) {
-      if (method.isPrivate || method.isStatic) continue;
-      _checkFunction(method.type, method.name, entryLib);
-    }*/
     var unstubbableErrorMessages = classElement.methods
         .where((m) => !m.isPrivate && !m.isStatic)
-        .expand((m) => _checkFunction(m.type, m.name, className, entryLib))
+        .expand((m) => _checkFunction(m.type, m.name, className))
         .toList();
 
     if (unstubbableErrorMessages.isNotEmpty) {
@@ -221,8 +240,8 @@
     }
   }
 
-  List<String> _checkFunction(analyzer.FunctionType function, String name,
-      String className, LibraryElement entryLib) {
+  List<String> _checkFunction(
+      analyzer.FunctionType function, String name, String className) {
     var errorMessages = <String>[];
     var returnType = function.returnType;
     if (returnType is analyzer.InterfaceType) {
@@ -232,11 +251,10 @@
                 'type, and cannot be stubbed.');
       }
     } else if (returnType is analyzer.FunctionType) {
-      errorMessages
-          .addAll(_checkFunction(returnType, name, className, entryLib));
+      errorMessages.addAll(_checkFunction(returnType, name, className));
     } else if (returnType is analyzer.TypeParameterType) {
       if (function.returnType is analyzer.TypeParameterType &&
-          entryLib.typeSystem.isPotentiallyNonNullable(function.returnType)) {
+          _entryLib.typeSystem.isPotentiallyNonNullable(function.returnType)) {
         errorMessages
             .add("The method '$className.$name' features a non-nullable "
                 'unknown return type, and cannot be stubbed.');
@@ -256,8 +274,7 @@
               "type, '${parameterTypeElement.name}', and cannot be stubbed.");
         }
       } else if (parameterType is analyzer.FunctionType) {
-        errorMessages
-            .addAll(_checkFunction(parameterType, name, className, entryLib));
+        errorMessages.addAll(_checkFunction(parameterType, name, className));
       }
     }
 
@@ -268,11 +285,6 @@
   bool _isMockClass(analyzer.InterfaceType type) =>
       type.element.name == 'Mock' &&
       type.element.source.fullName.endsWith('lib/src/mock.dart');
-
-  @override
-  final buildExtensions = const {
-    '.dart': ['.mocks.dart']
-  };
 }
 
 class _MockLibraryInfo {
@@ -500,7 +512,7 @@
 
     // This class is unknown; we must likely generate a fake class, and return
     // an instance here.
-    return _dummyValueImplementing(type);
+    return _dummyValueImplementing(type as analyzer.InterfaceType);
   }
 
   Expression _dummyFunctionValue(analyzer.FunctionType type) {