Throw an error if an arg matcher is used improperly; 4.1.2 (#216)

Throw an error if an arg matcher is used improperly
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f99597..58dc8e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 4.1.2
+
+* Produce a meaningful error message if an argument matcher is used outside of
+  stubbing (`when`) or verification (`verify` and `untilCalled`).
+
 ## 4.1.1
 
 * Mark the unexported and accidentally public `setDefaultResponse` as
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 1cb9f53..bbe63be 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -4,6 +4,12 @@
     implicit-casts: false
     implicit-dynamic: false
   errors:
+    # These are for 2.5.0-dev.2.0 and after
+    implicit_dynamic_list_literal: ignore
+    implicit_dynamic_map_literal: ignore
+    implicit_dynamic_parameter: ignore
+    implicit_dynamic_variable: ignore
+    # These are pre-2.5.0-dev.2.0
     strong_mode_implicit_dynamic_list_literal: ignore
     strong_mode_implicit_dynamic_map_literal: ignore
     strong_mode_implicit_dynamic_parameter: ignore
diff --git a/lib/src/mock.dart b/lib/src/mock.dart
index 11db403..e8c6c46 100644
--- a/lib/src/mock.dart
+++ b/lib/src/mock.dart
@@ -680,30 +680,33 @@
 }
 
 /// An argument matcher that matches any argument passed in "this" position.
-Null get any => _registerMatcher(anything, false);
+Null get any => _registerMatcher(anything, false, argumentMatcher: 'any');
 
 /// An argument matcher that matches any named argument passed in for the
 /// parameter named [named].
-Null anyNamed(String named) => _registerMatcher(anything, false, named: named);
+Null anyNamed(String named) => _registerMatcher(anything, false,
+    named: named, argumentMatcher: 'anyNamed');
 
 /// An argument matcher that matches any argument passed in "this" position, and
 /// captures the argument for later access with `captured`.
-Null get captureAny => _registerMatcher(anything, true);
+Null get captureAny =>
+    _registerMatcher(anything, true, argumentMatcher: 'captureAny');
 
 /// An argument matcher that matches any named argument passed in for the
 /// parameter named [named], and captures the argument for later access with
 /// `captured`.
-Null captureAnyNamed(String named) =>
-    _registerMatcher(anything, true, named: named);
+Null captureAnyNamed(String named) => _registerMatcher(anything, true,
+    named: named, argumentMatcher: 'captureAnyNamed');
 
 /// An argument matcher that matches an argument that matches [matcher].
 Null argThat(Matcher matcher, {String named}) =>
-    _registerMatcher(matcher, false, named: named);
+    _registerMatcher(matcher, false, named: named, argumentMatcher: 'argThat');
 
 /// An argument matcher that matches an argument that matches [matcher], and
 /// captures the argument for later access with `captured`.
 Null captureThat(Matcher matcher, {String named}) =>
-    _registerMatcher(matcher, true, named: named);
+    _registerMatcher(matcher, true,
+        named: named, argumentMatcher: 'captureThat');
 
 @Deprecated('ArgMatchers no longer need to be wrapped in Mockito 3.0')
 Null typed<T>(ArgMatcher matcher, {String named}) => null;
@@ -716,7 +719,27 @@
 Null typedCaptureThat(Matcher matcher, {String named}) =>
     captureThat(matcher, named: named);
 
-Null _registerMatcher(Matcher matcher, bool capture, {String named}) {
+/// Registers [matcher] into the stored arguments collections.
+///
+/// Creates an [ArgMatcher] with [matcher] and [capture], then if [named] is
+/// non-null, stores that into the positional stored arguments list; otherwise
+/// stores it into the named stored arguments map, keyed on [named].
+/// [argumentMatcher] is the name of the public API used to register [matcher],
+/// for error messages.
+Null _registerMatcher(Matcher matcher, bool capture,
+    {String named, String argumentMatcher}) {
+  if (!_whenInProgress && !_untilCalledInProgress && !_verificationInProgress) {
+    // It is not meaningful to store argument matchers outside of stubbing
+    // (`when`), or verification (`verify` and `untilCalled`). Such argument
+    // matchers will be processed later erroneously.
+    _storedArgs.clear();
+    _storedNamedArgs.clear();
+    throw ArgumentError(
+        'The "$argumentMatcher" argument matcher is used outside of method '
+        'stubbing (via `when`) or verification (via `verify` or `untilCalled`). '
+        'This is invalid, and results in bad behavior during the next stubbing '
+        'or verification.');
+  }
   var argMatcher = ArgMatcher(matcher, capture);
   if (named == null) {
     _storedArgs.add(argMatcher);
diff --git a/pubspec.yaml b/pubspec.yaml
index b291b6d..affbad8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: mockito
-version: 4.1.1
+version: 4.1.2
 
 authors:
   - Dmitriy Fibulwinter <fibulwinter@gmail.com>
diff --git a/test/mockito_test.dart b/test/mockito_test.dart
index ff872f4..99da123 100644
--- a/test/mockito_test.dart
+++ b/test/mockito_test.dart
@@ -296,4 +296,10 @@
       expect(() => mock.methodWithoutArgs(), returnsNormally);
     });
   });
+
+  test(
+      "reports an error when using an argument matcher outside of stubbing or "
+      "verification", () {
+    expect(() => mock.methodWithNormalArgs(any), throwsArgumentError);
+  });
 }