Migrate many custom matchers to TypeMatcher (#1668)

diff --git a/pkgs/file/pubspec.yaml b/pkgs/file/pubspec.yaml
index 5de5d37..0dc81cd 100644
--- a/pkgs/file/pubspec.yaml
+++ b/pkgs/file/pubspec.yaml
@@ -15,3 +15,7 @@
   file_testing: ^3.0.0
   lints: ^2.0.1
   test: ^1.23.1
+
+dependency_overrides:
+  file_testing:
+    path: ../file_testing
diff --git a/pkgs/file_testing/CHANGELOG.md b/pkgs/file_testing/CHANGELOG.md
index 0af779d..17039ee 100644
--- a/pkgs/file_testing/CHANGELOG.md
+++ b/pkgs/file_testing/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 3.1.0-wip
+
+* Changed the type of several matchers to `TypeMatcher` which allows cascading
+  their usage with `.having` and similar. 
+
 ## 3.0.2
 
 * Require Dart 3.1.
diff --git a/pkgs/file_testing/analysis_options.yaml b/pkgs/file_testing/analysis_options.yaml
index 8fbd2e4..d978f81 100644
--- a/pkgs/file_testing/analysis_options.yaml
+++ b/pkgs/file_testing/analysis_options.yaml
@@ -1,6 +1 @@
-include: package:lints/recommended.yaml
-
-analyzer:
-  errors:
-    # Allow having TODOs in the code
-    todo: ignore
+include: package:dart_flutter_team_lints/analysis_options.yaml
diff --git a/pkgs/file_testing/lib/src/testing/core_matchers.dart b/pkgs/file_testing/lib/src/testing/core_matchers.dart
index f58539f..801209e 100644
--- a/pkgs/file_testing/lib/src/testing/core_matchers.dart
+++ b/pkgs/file_testing/lib/src/testing/core_matchers.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// ignore_for_file: comment_references
+
 import 'dart:io';
 
 import 'package:test/test.dart';
@@ -9,26 +11,27 @@
 import 'internal.dart';
 
 /// Matcher that successfully matches against any instance of [Directory].
-const Matcher isDirectory = TypeMatcher<Directory>();
+const isDirectory = TypeMatcher<Directory>();
 
 /// Matcher that successfully matches against any instance of [File].
-const Matcher isFile = TypeMatcher<File>();
+const isFile = TypeMatcher<File>();
 
 /// Matcher that successfully matches against any instance of [Link].
-const Matcher isLink = TypeMatcher<Link>();
+const isLink = TypeMatcher<Link>();
 
 /// Matcher that successfully matches against any instance of
 /// [FileSystemEntity].
-const Matcher isFileSystemEntity = TypeMatcher<FileSystemEntity>();
+const isFileSystemEntity = TypeMatcher<FileSystemEntity>();
 
 /// Matcher that successfully matches against any instance of [FileStat].
-const Matcher isFileStat = TypeMatcher<FileStat>();
+const isFileStat = TypeMatcher<FileStat>();
 
 /// Returns a [Matcher] that matches [path] against an entity's path.
 ///
 /// [path] may be a String, a predicate function, or a [Matcher]. If it is
 /// a String, it will be wrapped in an equality matcher.
-Matcher hasPath(dynamic path) => _HasPath(path);
+TypeMatcher<FileSystemEntity> hasPath(dynamic path) =>
+    isFileSystemEntity.having((e) => e.path, 'path', path);
 
 /// Returns a [Matcher] that successfully matches against an instance of
 /// [FileSystemException].
@@ -39,7 +42,8 @@
 /// [osErrorCode] may be an `int`, a predicate function, or a [Matcher]. If it
 /// is an `int`, it will be wrapped in an equality matcher.
 Matcher isFileSystemException([dynamic osErrorCode]) =>
-    _FileSystemException(osErrorCode);
+    const TypeMatcher<FileSystemException>().having((e) => e.osError?.errorCode,
+        'osError.errorCode', _fileExceptionWrapMatcher(osErrorCode));
 
 /// Returns a matcher that successfully matches against a future or function
 /// that throws a [FileSystemException].
@@ -67,89 +71,10 @@
 
 /// Matcher that successfully matches against a [FileSystemEntity] that
 /// exists ([FileSystemEntity.existsSync] returns true).
-const Matcher exists = _Exists();
+final TypeMatcher<FileSystemEntity> exists =
+    isFileSystemEntity.having((e) => e.existsSync(), 'existsSync', true);
 
-class _FileSystemException extends Matcher {
-  _FileSystemException(dynamic osErrorCode)
-      : _matcher = _wrapMatcher(osErrorCode);
-
-  final Matcher? _matcher;
-
-  static Matcher? _wrapMatcher(dynamic osErrorCode) {
-    if (osErrorCode == null) {
-      return null;
-    }
-    return ignoreOsErrorCodes ? anything : wrapMatcher(osErrorCode);
-  }
-
-  @override
-  bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
-    if (item is FileSystemException) {
-      return _matcher == null ||
-          _matcher!.matches(item.osError?.errorCode, matchState);
-    }
-    return false;
-  }
-
-  @override
-  Description describe(Description desc) {
-    if (_matcher == null) {
-      return desc.add('FileSystemException');
-    } else {
-      desc.add('FileSystemException with osError.errorCode: ');
-      return _matcher!.describe(desc);
-    }
-  }
-}
-
-class _HasPath extends Matcher {
-  _HasPath(dynamic path) : _matcher = wrapMatcher(path);
-
-  final Matcher _matcher;
-
-  @override
-  bool matches(dynamic item, Map<dynamic, dynamic> matchState) =>
-      _matcher.matches(item.path, matchState);
-
-  @override
-  Description describe(Description desc) {
-    desc.add('has path: ');
-    return _matcher.describe(desc);
-  }
-
-  @override
-  Description describeMismatch(
-    dynamic item,
-    Description desc,
-    Map<dynamic, dynamic> matchState,
-    bool verbose,
-  ) {
-    desc.add('has path: \'${item.path}\'').add('\n   Which: ');
-    final Description pathDesc = StringDescription();
-    _matcher.describeMismatch(item.path, pathDesc, matchState, verbose);
-    desc.add(pathDesc.toString());
-    return desc;
-  }
-}
-
-class _Exists extends Matcher {
-  const _Exists();
-
-  @override
-  bool matches(dynamic item, Map<dynamic, dynamic> matchState) =>
-      item is FileSystemEntity && item.existsSync();
-
-  @override
-  Description describe(Description description) =>
-      description.add('a file system entity that exists');
-
-  @override
-  Description describeMismatch(
-    dynamic item,
-    Description description,
-    Map<dynamic, dynamic> matchState,
-    bool verbose,
-  ) {
-    return description.add('does not exist');
-  }
-}
+Matcher? _fileExceptionWrapMatcher(dynamic osErrorCode) =>
+    (osErrorCode == null || ignoreOsErrorCodes)
+        ? anything
+        : wrapMatcher(osErrorCode);
diff --git a/pkgs/file_testing/pubspec.yaml b/pkgs/file_testing/pubspec.yaml
index 691efa0..895826a 100644
--- a/pkgs/file_testing/pubspec.yaml
+++ b/pkgs/file_testing/pubspec.yaml
@@ -1,5 +1,5 @@
 name: file_testing
-version: 3.0.2
+version: 3.1.0-wip
 description: Testing utilities for package:file.
 repository: https://github.com/dart-lang/tools/tree/main/pkgs/file_testing
 issue_tracker: https://github.com/dart-lang/tools/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Afile_testing
@@ -10,5 +10,5 @@
 dependencies:
   test: ^1.23.1
 
-dev_dependencies: 
-  lints: ^5.0.0
+dev_dependencies:
+  dart_flutter_team_lints: ^3.0.0