Skip to content

Commit fadf8a0

Browse files
DanTupCommit Queue
authored and
Commit Queue
committed
[analysis_server] Support LSP executeCommand over DTD to support code actions
There are a few things in this CL that were difficult to separate testing of (because they're all part of the same feature): 1. sets `requiresTrustedCaller=false` on the `executeCommand` handler so that commands can be called over DTD. It adds the same flag to the command handlers themselves, so a command can control whether it can be called over DTD or not (right now we allow everything except `logAction` and `sendWorkspaceEdit` which are both commands used internally and not appropriate for DTD clients to call). 2. Removes the allow-list on DTD methods, allowing all LSP shared methods to be available over DTD 3. Extends the integration test classes to support reverse-requests so we can verify the edits being sent back to the editor when calling the code actions commands over DTD 4. It also fixes a few bugs where we read the callers capabilities instead of the editors capabilities (which until now would always be the same in those places, but with this change are not). Change-Id: I6d271ddad6dc1b00a98b10b735763a368c91af7a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428784 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent a3b1b4f commit fadf8a0

27 files changed

+379
-68
lines changed

pkg/analysis_server/lib/src/handler/legacy/lsp_over_legacy_handler.dart

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class LspOverLegacyHandler extends LegacyHandler {
6969
performance: performance,
7070
clientCapabilities: server.editorClientCapabilities,
7171
timeSinceRequest: request.timeSinceRequest,
72+
isTrustedCaller: true,
7273
);
7374

7475
ErrorOr<Object?> result;

pkg/analysis_server/lib/src/lsp/handlers/commands/apply_code_action.dart

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class ApplyCodeActionCommandHandler
3333
@override
3434
bool get recordsOwnAnalytics => true;
3535

36+
@override
37+
bool get requiresTrustedCaller => false;
38+
3639
@override
3740
Future<ErrorOr<void>> handle(
3841
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/fix_all.dart

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class FixAllCommandHandler extends SimpleEditCommandHandler<LspAnalysisServer> {
2222
@override
2323
String get commandName => 'Fix All';
2424

25+
@override
26+
bool get requiresTrustedCaller => false;
27+
2528
@override
2629
Future<ErrorOr<void>> handle(
2730
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/fix_all_in_workspace.dart

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ abstract class AbstractFixAllInWorkspaceCommandHandler
2424
/// user can choose which changes to apply.
2525
bool get requireConfirmation;
2626

27+
@override
28+
bool get requiresTrustedCaller => false;
29+
2730
@override
2831
Future<ErrorOr<void>> handle(
2932
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/log_action.dart

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ class LogActionCommandHandler
1515
@override
1616
bool get recordsOwnAnalytics => true;
1717

18+
@override
19+
// We only currently expect this to be called by the editor.
20+
bool get requiresTrustedCaller => true;
21+
1822
@override
1923
Future<ErrorOr<void>> handle(
2024
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/organize_imports.dart

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class OrganizeImportsCommandHandler extends SimpleEditCommandHandler {
1616
@override
1717
String get commandName => 'Organize Imports';
1818

19+
@override
20+
bool get requiresTrustedCaller => false;
21+
1922
@override
2023
Future<ErrorOr<void>> handle(
2124
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart

+10-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ class PerformRefactorCommandHandler extends AbstractRefactorCommandHandler {
2828
@override
2929
bool get recordsOwnAnalytics => true;
3030

31+
@override
32+
bool get requiresTrustedCaller => false;
33+
3134
@override
3235
FutureOr<ErrorOr<void>> execute(
3336
String path,
@@ -40,6 +43,12 @@ class PerformRefactorCommandHandler extends AbstractRefactorCommandHandler {
4043
ProgressReporter reporter,
4144
int? docVersion,
4245
) async {
46+
var editorCapabilities = server.editorClientCapabilities;
47+
if (editorCapabilities == null) {
48+
// This should not happen unless a client misbehaves.
49+
return serverNotInitializedError;
50+
}
51+
4352
var actionName = 'dart.refactor.${kind.toLowerCase()}';
4453
server.analyticsManager.executedCommand(actionName);
4554

@@ -102,7 +111,7 @@ class PerformRefactorCommandHandler extends AbstractRefactorCommandHandler {
102111
return fileModifiedError;
103112
}
104113

105-
var edit = createWorkspaceEdit(server, clientCapabilities, change);
114+
var edit = createWorkspaceEdit(server, editorCapabilities, change);
106115
return await sendWorkspaceEditToClient(edit);
107116
} finally {
108117
manager.end(cancelableToken);

pkg/analysis_server/lib/src/lsp/handlers/commands/refactor_command_handler.dart

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class RefactorCommandHandler extends SimpleEditCommandHandler<AnalysisServer> {
2727

2828
RefactorCommandHandler(super.server, this.commandName, this.generator);
2929

30+
@override
31+
bool get requiresTrustedCaller => false;
32+
3033
@override
3134
Future<ErrorOr<void>> handle(
3235
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/send_workspace_edit.dart

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ class SendWorkspaceEditCommandHandler extends SimpleEditCommandHandler {
2222
@override
2323
String get commandName => 'Send Workspace Edit';
2424

25+
@override
26+
// This command is used internally to work around some limitations in LSP
27+
// and is not expected to ever be called by a DTD client.
28+
bool get requiresTrustedCaller => true;
29+
2530
@override
2631
Future<ErrorOr<void>> handle(
2732
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/sort_members.dart

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class SortMembersCommandHandler extends SimpleEditCommandHandler {
1717
@override
1818
String get commandName => 'Sort Members';
1919

20+
@override
21+
bool get requiresTrustedCaller => false;
22+
2023
@override
2124
Future<ErrorOr<void>> handle(
2225
MessageInfo message,

pkg/analysis_server/lib/src/lsp/handlers/commands/validate_refactor.dart

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class ValidateRefactorCommandHandler extends AbstractRefactorCommandHandler {
2222
@override
2323
bool get recordsOwnAnalytics => true;
2424

25+
@override
26+
bool get requiresTrustedCaller => false;
27+
2528
@override
2629
FutureOr<ErrorOr<ValidateRefactorResult>> execute(
2730
String path,

pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart

+13-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'package:analysis_server/lsp_protocol/protocol.dart';
5+
import 'dart:async';
6+
7+
import 'package:analysis_server/lsp_protocol/protocol.dart' hide MessageType;
68
import 'package:analysis_server/src/analysis_server.dart';
79
import 'package:analysis_server/src/lsp/constants.dart';
810
import 'package:analysis_server/src/lsp/error_or.dart';
@@ -65,10 +67,9 @@ class ExecuteCommandHandler
6567
ExecuteCommandParams.jsonHandler;
6668

6769
@override
68-
// TODO(dantup): This will need to be relaxed to support calling over DTD,
69-
// but at that point we must also add this flag to Commands so that we can
70-
// control which commands require trusted callers.
71-
bool get requiresTrustedCaller => true;
70+
/// This handler does not require a trusted caller, however some of the
71+
/// commands might (which are checked on the handler during execution).
72+
bool get requiresTrustedCaller => false;
7273

7374
@override
7475
Future<ErrorOr<Object?>> handle(
@@ -84,6 +85,13 @@ class ExecuteCommandHandler
8485
);
8586
}
8687

88+
if (handler.requiresTrustedCaller && !message.isTrustedCaller) {
89+
return error(
90+
ServerErrorCodes.UnknownCommand,
91+
'${params.command} can only be called by the owning process',
92+
);
93+
}
94+
8795
if (!handler.recordsOwnAnalytics) {
8896
server.analyticsManager.executedCommand(params.command);
8997
}

pkg/analysis_server/lib/src/lsp/handlers/handler_will_rename_files.dart

+7-5
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ class WillRenameFilesHandler
3939
MessageInfo message,
4040
CancellationToken token,
4141
) async {
42-
var clientCapabilities = message.clientCapabilities;
43-
if (clientCapabilities == null) {
42+
// Use editor capabilities because this is used to compute to edits to send
43+
// to the client.
44+
var editorCapabilities = server.editorClientCapabilities;
45+
if (editorCapabilities == null) {
4446
return serverNotInitializedError;
4547
}
4648

@@ -61,11 +63,11 @@ class WillRenameFilesHandler
6163
pathMapping[oldPath] = newPath;
6264
});
6365
}
64-
return _renameFiles(clientCapabilities, pathMapping, token);
66+
return _renameFiles(editorCapabilities, pathMapping, token);
6567
}
6668

6769
Future<ErrorOr<WorkspaceEdit?>> _renameFiles(
68-
LspClientCapabilities clientCapabilities,
70+
LspClientCapabilities editorCapabilities,
6971
Map<String, String> renames,
7072
CancellationToken token,
7173
) async {
@@ -103,7 +105,7 @@ class WillRenameFilesHandler
103105

104106
server.checkConsistency(sessions);
105107

106-
var edit = createWorkspaceEdit(server, clientCapabilities, change);
108+
var edit = createWorkspaceEdit(server, editorCapabilities, change);
107109
return success(edit);
108110
}
109111
}

pkg/analysis_server/lib/src/lsp/handlers/handlers.dart

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ abstract class CommandHandler<P, R, S extends AnalysisServer>
5050
/// can record a more specific command name.
5151
bool get recordsOwnAnalytics => false;
5252

53+
/// Whether or not this handler can only be called by the owner of the
54+
/// analysis server process (for example the editor).
55+
bool get requiresTrustedCaller;
56+
5357
Future<ErrorOr<Object?>> handle(
5458
MessageInfo message,
5559
Map<String, Object?> parameters,
@@ -455,13 +459,18 @@ class MessageInfo {
455459
/// shown.
456460
final Completer<void>? completer;
457461

462+
/// Whether or not this message came from the the owner of the analysis server
463+
/// process (for example the editor).
464+
final bool isTrustedCaller;
465+
458466
MessageInfo({
459467
required this.performance,
460468
// TODO(dantup): Consider a version of this that has a non-nullable
461469
// `LspClientCapabilities` since the majority of handlers first check this
462470
// is non-null and reject the request. Only a small number of handlers need
463471
// to run without, so it would remove a bunch of boilerplate in the others.
464472
required this.clientCapabilities,
473+
required this.isTrustedCaller,
465474
this.timeSinceRequest,
466475
this.completer,
467476
});

pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart

+1
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ class LspAnalysisServer extends AnalysisServer {
509509
clientCapabilities: editorClientCapabilities,
510510
timeSinceRequest: message.timeSinceRequest,
511511
completer: completer,
512+
isTrustedCaller: true,
512513
);
513514

514515
if (message is RequestMessage) {

pkg/analysis_server/lib/src/services/dart_tooling_daemon/dtd_services.dart

+4-19
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'dart:async';
77
import 'package:analysis_server/lsp_protocol/protocol.dart';
88
import 'package:analysis_server/src/analysis_server.dart';
99
import 'package:analysis_server/src/lsp/client_capabilities.dart';
10-
import 'package:analysis_server/src/lsp/constants.dart';
1110
import 'package:analysis_server/src/lsp/error_or.dart';
1211
import 'package:analysis_server/src/lsp/handlers/handler_states.dart';
1312
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
@@ -36,21 +35,6 @@ enum DtdConnectionState {
3635
/// A connection to DTD that exposes some analysis services (such as a subset
3736
/// of LSP) to other DTD clients.
3837
class DtdServices {
39-
/// The set of LSP methods currently exposed over DTD.
40-
///
41-
/// Eventually all "shared" methods will be exposed, but during initial
42-
/// development and testing this will be restricted to selected methods (in
43-
/// particular, those with well defined results that are not affected by
44-
/// differences in client capabilities).
45-
static final allowedLspMethods = <Method>{
46-
// TODO(dantup): Remove this allow list so that all shared methods are
47-
// available over DTD.
48-
CustomMethods.experimentalEcho,
49-
CustomMethods.dartTextDocumentEditableArguments,
50-
CustomMethods.dartTextDocumentEditArgument,
51-
Method.textDocument_hover,
52-
};
53-
5438
/// The name of the DTD service that methods will be registered under.
5539
static const _lspServiceName = 'Lsp';
5640

@@ -92,6 +76,7 @@ class DtdServices {
9276
// capabilities so that the responses don't change in format based on the
9377
// owning editor.
9478
clientCapabilities: fixedBasicLspClientCapabilities,
79+
isTrustedCaller: false,
9580
);
9681
var token = NotCancelableToken(); // We don't currently support cancel.
9782

@@ -230,8 +215,7 @@ class DtdServices {
230215
) async {
231216
await Future.wait([
232217
for (var lspHandler in handler.messageHandlers.values)
233-
if (allowedLspMethods.contains(lspHandler.handlesMessage) &&
234-
(registerExperimentalHandlers || !lspHandler.isExperimental))
218+
if (registerExperimentalHandlers || !lspHandler.isExperimental)
235219
_registerLspService(lspHandler, dtd),
236220
]);
237221

@@ -240,7 +224,8 @@ class DtdServices {
240224
await dtd.postEvent(_lspStreamName, 'initialized', {});
241225
}
242226

243-
/// Registers a single message handler to DTD if it allows untrusted callers.
227+
/// Registers a single message handler to DTD only if it allows untrusted
228+
/// callers.
244229
Future<void> _registerLspService(
245230
MessageHandler<Object?, Object?, AnalysisServer> messageHandler,
246231
DartToolingDaemon dtd,

pkg/analysis_server/lib/src/services/user_prompts/dart_fix_prompt_manager.dart

+1
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ class DartFixPromptManager {
256256
MessageInfo(
257257
performance: OperationPerformanceImpl(''),
258258
clientCapabilities: clientCapabilities,
259+
isTrustedCaller: true,
259260
),
260261
NotCancelableToken(),
261262
);

pkg/analysis_server/test/integration/dtd/dtd_test.dart

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ class DtdTest
3333
Future<void> initializeServer() async {
3434
await standardAnalysisSetup();
3535
await analysisFinished;
36+
await sendServerSetClientCapabilities(
37+
[],
38+
lspCapabilities: editorClientCapabilities.raw,
39+
);
3640
}
3741

3842
@override

0 commit comments

Comments
 (0)