[flutter_tools] consume package:process exceptions (#79657)

diff --git a/packages/flutter_tools/lib/src/base/error_handling_io.dart b/packages/flutter_tools/lib/src/base/error_handling_io.dart
index c00036c..e369c09 100644
--- a/packages/flutter_tools/lib/src/base/error_handling_io.dart
+++ b/packages/flutter_tools/lib/src/base/error_handling_io.dart
@@ -533,6 +533,8 @@
   String toString() => delegate.toString();
 }
 
+const String _kNoExecutableFound = 'The Flutter tool could not locate an executable with suitable permissions';
+
 Future<T> _run<T>(Future<T> Function() op, {
   required Platform platform,
   String? failureMessage,
@@ -540,6 +542,11 @@
   assert(platform != null);
   try {
     return await op();
+  } on ProcessPackageExecutableNotFoundException catch (e) {
+    if (e.candidates.isNotEmpty) {
+      throwToolExit('$_kNoExecutableFound: $e');
+    }
+    rethrow;
   } on FileSystemException catch (e) {
     if (platform.isWindows) {
       _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
@@ -564,6 +571,11 @@
   assert(platform != null);
   try {
     return op();
+  } on ProcessPackageExecutableNotFoundException catch (e) {
+    if (e.candidates.isNotEmpty) {
+      throwToolExit('$_kNoExecutableFound: $e');
+    }
+    rethrow;
   } on FileSystemException catch (e) {
     if (platform.isWindows) {
       _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
@@ -581,69 +593,6 @@
   }
 }
 
-class _ProcessDelegate {
-  const _ProcessDelegate();
-
-  Future<io.Process> start(
-    List<String> command, {
-    String? workingDirectory,
-    Map<String, String>? environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    io.ProcessStartMode mode = io.ProcessStartMode.normal,
-  }) {
-    return io.Process.start(
-      command[0],
-      command.skip(1).toList(),
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-    );
-  }
-
-  Future<io.ProcessResult> run(
-    List<String> command, {
-    String? workingDirectory,
-    Map<String, String>? environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    Encoding stdoutEncoding = io.systemEncoding,
-    Encoding stderrEncoding = io.systemEncoding,
-  }) {
-    return io.Process.run(
-      command[0],
-      command.skip(1).toList(),
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      stdoutEncoding: stdoutEncoding,
-      stderrEncoding: stderrEncoding,
-    );
-  }
-
-  io.ProcessResult runSync(
-    List<String> command, {
-    String? workingDirectory,
-    Map<String, String>? environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    Encoding stdoutEncoding = io.systemEncoding,
-    Encoding stderrEncoding = io.systemEncoding,
-  }) {
-    return io.Process.runSync(
-      command[0],
-      command.skip(1).toList(),
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      stdoutEncoding: stdoutEncoding,
-      stderrEncoding: stderrEncoding,
-    );
-  }
-}
 
 /// A [ProcessManager] that throws a [ToolExit] on certain errors.
 ///
@@ -662,21 +611,6 @@
 
   final ProcessManager _delegate;
   final Platform _platform;
-  static const _ProcessDelegate _processDelegate = _ProcessDelegate();
-  static bool _skipCommandLookup = false;
-
-  /// Bypass package:process command lookup for all functions in this block.
-  ///
-  /// This required that the fully resolved executable path is provided.
-  static Future<T> skipCommandLookup<T>(Future<T> Function() operation) async {
-    final bool previousValue = ErrorHandlingProcessManager._skipCommandLookup;
-    try {
-      ErrorHandlingProcessManager._skipCommandLookup = true;
-      return await operation();
-    } finally {
-      ErrorHandlingProcessManager._skipCommandLookup = previousValue;
-    }
-  }
 
   @override
   bool canRun(dynamic executable, {String? workingDirectory}) {
@@ -705,17 +639,6 @@
     Encoding stderrEncoding = io.systemEncoding,
   }) {
     return _run(() {
-      if (_skipCommandLookup && _delegate is LocalProcessManager) {
-       return _processDelegate.run(
-          command.cast<String>(),
-          workingDirectory: workingDirectory,
-          environment: environment,
-          includeParentEnvironment: includeParentEnvironment,
-          runInShell: runInShell,
-          stdoutEncoding: stdoutEncoding,
-          stderrEncoding: stderrEncoding,
-        );
-      }
       return _delegate.run(
         command,
         workingDirectory: workingDirectory,
@@ -738,15 +661,6 @@
     io.ProcessStartMode mode = io.ProcessStartMode.normal,
   }) {
     return _run(() {
-      if (_skipCommandLookup && _delegate is LocalProcessManager) {
-        return _processDelegate.start(
-          command.cast<String>(),
-          workingDirectory: workingDirectory,
-          environment: environment,
-          includeParentEnvironment: includeParentEnvironment,
-          runInShell: runInShell,
-        );
-      }
       return _delegate.start(
         command,
         workingDirectory: workingDirectory,
@@ -768,17 +682,6 @@
     Encoding stderrEncoding = io.systemEncoding,
   }) {
     return _runSync(() {
-      if (_skipCommandLookup && _delegate is LocalProcessManager) {
-        return _processDelegate.runSync(
-          command.cast<String>(),
-          workingDirectory: workingDirectory,
-          environment: environment,
-          includeParentEnvironment: includeParentEnvironment,
-          runInShell: runInShell,
-          stdoutEncoding: stdoutEncoding,
-          stderrEncoding: stderrEncoding,
-        );
-      }
       return _delegate.runSync(
         command,
         workingDirectory: workingDirectory,
diff --git a/packages/flutter_tools/lib/src/dart/pub.dart b/packages/flutter_tools/lib/src/dart/pub.dart
index 535a08b..f48658e 100644
--- a/packages/flutter_tools/lib/src/dart/pub.dart
+++ b/packages/flutter_tools/lib/src/dart/pub.dart
@@ -11,7 +11,6 @@
 import '../base/bot_detector.dart';
 import '../base/common.dart';
 import '../base/context.dart';
-import '../base/error_handling_io.dart';
 import '../base/file_system.dart';
 import '../base/io.dart' as io;
 import '../base/logger.dart';
@@ -334,13 +333,11 @@
     bool generateSyntheticPackage = false,
   }) async {
     // Fully resolved pub or pub.bat is calculated based on current platform.
-    final io.Process process = await ErrorHandlingProcessManager.skipCommandLookup(() async {
-      return _processUtils.start(
-        _pubCommand(arguments),
-        workingDirectory: directory,
-        environment: await _createPubEnvironment(PubContext.interactive),
-      );
-    });
+    final io.Process process = await _processUtils.start(
+      _pubCommand(arguments),
+      workingDirectory: directory,
+      environment: await _createPubEnvironment(PubContext.interactive),
+    );
 
     // Pipe the Flutter tool stdin to the pub stdin.
     unawaited(process.stdin.addStream(stdio.stdin)
diff --git a/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart b/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart
index ae4f2fa..4281522 100644
--- a/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart
@@ -684,25 +684,47 @@
     });
   });
 
-  test('skipCommandLookup invokes Process calls directly', () async {
-    final ErrorHandlingProcessManager processManager = ErrorHandlingProcessManager(
-      delegate: const LocalProcessManager(),
-      platform: windowsPlatform,
-    );
-
-    // Throws process exception because the executable does not exist.
-    await ErrorHandlingProcessManager.skipCommandLookup<void>(() async {
-      expect(() => processManager.runSync(<String>['foo']), throwsA(isA<ProcessException>()));
-      expect(() => processManager.run(<String>['foo']), throwsA(isA<ProcessException>()));
-      expect(() => processManager.start(<String>['foo']), throwsA(isA<ProcessException>()));
-    });
-  });
-
   group('ProcessManager on windows throws tool exit', () {
     const int kDeviceFull = 112;
     const int kUserMappedSectionOpened = 1224;
     const int kUserPermissionDenied = 5;
 
+    test('when PackageProcess throws an exception containg non-executable bits', () {
+      final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
+        const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>['not-empty'])),
+        const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>['not-empty'])),
+      ]);
+
+      final ProcessManager processManager = ErrorHandlingProcessManager(
+        delegate: fakeProcessManager,
+        platform: windowsPlatform,
+      );
+
+      const String expectedMessage = 'The Flutter tool could not locate an executable with suitable permissions';
+
+      expect(() async => processManager.start(<String>['foo']),
+             throwsToolExit(message: expectedMessage));
+      expect(() async => processManager.runSync(<String>['foo']),
+             throwsToolExit(message: expectedMessage));
+    });
+
+    test('when PackageProcess throws an exception without containing non-executable bits', () {
+      final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
+        const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>[])),
+        const FakeCommand(command: <String>['foo'], exception: ProcessPackageExecutableNotFoundException('', candidates: <String>[])),
+      ]);
+
+      final ProcessManager processManager = ErrorHandlingProcessManager(
+        delegate: fakeProcessManager,
+        platform: windowsPlatform,
+      );
+
+      // If there were no located executables treat this as a programming error and rethrow the original
+      // exception.
+      expect(() async => processManager.start(<String>['foo']), throwsProcessException());
+      expect(() async => processManager.runSync(<String>['foo']), throwsProcessException());
+    });
+
     test('when the device is full', () {
       final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
         const FakeCommand(command: <String>['foo'], exception: ProcessException('', <String>[], '', kDeviceFull)),