Version 2.18.0-161.0.dev

Merge commit '75d622a25d5928e62ee10ee12534648dd95963d0' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/constants.dart b/pkg/analysis_server/lib/src/lsp/constants.dart
index de28ea1..a3a718d 100644
--- a/pkg/analysis_server/lib/src/lsp/constants.dart
+++ b/pkg/analysis_server/lib/src/lsp/constants.dart
@@ -214,7 +214,6 @@
   static const FileHasErrors = ErrorCodes(-32008);
   static const ClientFailedToApplyEdit = ErrorCodes(-32009);
   static const RenameNotValid = ErrorCodes(-32010);
-  static const RefactorFailed = ErrorCodes(-32011);
   static const FeatureDisabled = ErrorCodes(-32012);
 
   /// A file that is expected to be analyzed, but failed.
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
index 018892a..a27fa77 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
@@ -5,7 +5,6 @@
 import 'dart:async';
 
 import 'package:analysis_server/lsp_protocol/protocol.dart';
-import 'package:analysis_server/src/lsp/constants.dart';
 import 'package:analysis_server/src/lsp/handlers/commands/abstract_refactor.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/mapping.dart';
@@ -48,7 +47,11 @@
           final status = await refactoring.checkAllConditions();
 
           if (status.hasError) {
-            return error(ServerErrorCodes.RefactorFailed, status.message!);
+            // Show the error to the user but don't fail the request, as the
+            // LSP Client may show a failed request in a way that looks like a
+            // server error.
+            server.showErrorMessageToUser(status.message!);
+            return success(null);
           }
 
           if (cancellationToken.isCancellationRequested ||
diff --git a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
index 4fb647d..029f542 100644
--- a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -475,6 +475,52 @@
     expect(response.message, contains('Cannot extract closure as method'));
   }
 
+  /// Test if the client does not call refactor.validate it still gets a
+  /// sensible `showMessage` call and not a failed request.
+  Future<void> test_validLocation_failsInitialValidation_noValidation() async {
+    const content = '''
+f() {
+  var a = 0;
+  doFoo([[() => print(a)]]);
+  print(a);
+}
+
+void doFoo(void Function() a) => a();
+
+    ''';
+    newFile(mainFilePath, withoutMarkers(content));
+    await initialize(
+      // We expect an error notification so don't fail on it.
+      failTestOnAnyErrorNotification: false,
+    );
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        range: rangeFromMarkers(content));
+    final codeAction =
+        findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
+
+    final command = codeAction.map(
+      (command) => command,
+      (codeAction) => codeAction.command!,
+    );
+
+    // Call the refactor without any validation and expected an error message
+    // without a request failure.
+    final errorNotification = await expectErrorNotification(() async {
+      final response = await executeCommand(
+        Command(
+            title: command.title,
+            command: command.command,
+            arguments: command.arguments),
+      );
+      expect(response, isNull);
+    });
+    expect(
+      errorNotification.message,
+      contains('Cannot extract closure as method'),
+    );
+  }
+
   Future<void> test_validLocation_passesInitialValidation() async {
     const content = '''
 f() {
diff --git a/pkg/analysis_server/test/mocks_lsp.dart b/pkg/analysis_server/test/mocks_lsp.dart
index 07358d8..857639f 100644
--- a/pkg/analysis_server/test/mocks_lsp.dart
+++ b/pkg/analysis_server/test/mocks_lsp.dart
@@ -8,8 +8,6 @@
 import 'package:analysis_server/lsp_protocol/protocol.dart' as lsp;
 import 'package:analysis_server/src/lsp/channel/lsp_channel.dart';
 
-const _jsonEncoder = JsonEncoder.withIndent('    ');
-
 /// A mock [LspServerCommunicationChannel] for testing [LspAnalysisServer].
 class MockLspServerChannel implements LspServerCommunicationChannel {
   final StreamController<lsp.Message> _clientToServer =
@@ -160,22 +158,14 @@
   /// received response. The returned future will throw an exception if a server
   /// error is reported before the response has been received.
   ///
-  /// Unlike [sendLspRequest], this method assumes that the [request] has
+  /// Unlike [sendRequestToServer], this method assumes that the [request] has
   /// already been sent to the server.
   Future<lsp.ResponseMessage> waitForResponse(
-    lsp.RequestMessage request, {
-    bool throwOnError = true,
-  }) async {
+      lsp.RequestMessage request) async {
     final response = await _serverToClient.stream.firstWhere((message) =>
-        (message is lsp.ResponseMessage && message.id == request.id) ||
-        (throwOnError && _isShowErrorMessageNotification(message)));
+        message is lsp.ResponseMessage && message.id == request.id);
 
-    if (response is lsp.ResponseMessage) {
-      return response;
-    } else {
-      throw 'An error occurred while waiting for a response to ${request.method}: '
-          '${_jsonEncoder.convert(response.toJson())}';
-    }
+    return response as lsp.ResponseMessage;
   }
 
   /// Round trips the object to JSON and back to ensure it behaves the same as
@@ -187,19 +177,4 @@
     return constructor(
         jsonDecode(jsonEncode(message.toJson())) as Map<String, Object?>);
   }
-
-  /// Checks whether [message] is a `window/showMessage` notification with a
-  /// type of [lsp.MessageType.Error].
-  bool _isShowErrorMessageNotification(lsp.Message message) {
-    if (message is! lsp.NotificationMessage) {
-      return false;
-    }
-    if (message.method != lsp.Method.window_showMessage) {
-      return false;
-    }
-    final params = lsp.ShowMessageParams.fromJson(
-      message.params as Map<String, Object?>,
-    );
-    return params.type == lsp.MessageType.Error;
-  }
 }
diff --git a/tools/VERSION b/tools/VERSION
index 50b2665..ca2aee4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 160
+PRERELEASE 161
 PRERELEASE_PATCH 0
\ No newline at end of file