Version 2.14.0-26.0.dev
Merge commit 'a808d5ca20b38216b3617b21349b8530c6c5cbff' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
index bd38471..1196205 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -11,21 +11,44 @@
class JsUtilOptimizer extends Transformer {
final Procedure _jsTarget;
final Procedure _getPropertyTarget;
+ final Procedure _setPropertyTarget;
+
+ /// Dynamic members in js_util that interop allowed.
+ static final Iterable<String> _allowedInteropJsUtilMembers = <String>[
+ 'callConstructor',
+ 'callMethod',
+ 'getProperty',
+ 'jsify',
+ 'newObject',
+ 'setProperty'
+ ];
+ final Iterable<Procedure> _allowedInteropJsUtilTargets;
+ final Procedure _allowInteropTarget;
JsUtilOptimizer(CoreTypes coreTypes)
: _jsTarget =
coreTypes.index.getTopLevelMember('dart:_foreign_helper', 'JS'),
_getPropertyTarget =
- coreTypes.index.getTopLevelMember('dart:js_util', 'getProperty') {}
+ coreTypes.index.getTopLevelMember('dart:js_util', 'getProperty'),
+ _setPropertyTarget =
+ coreTypes.index.getTopLevelMember('dart:js_util', 'setProperty'),
+ _allowInteropTarget =
+ coreTypes.index.getTopLevelMember('dart:js', 'allowInterop'),
+ _allowedInteropJsUtilTargets = _allowedInteropJsUtilMembers.map(
+ (member) =>
+ coreTypes.index.getTopLevelMember('dart:js_util', member)) {}
/// Replaces js_util method calls with lowering straight to JS fragment call.
///
/// Lowers the following types of js_util calls:
/// - `getProperty` for any argument types
+ /// - `setProperty` for values that are guaranteed to be interop allowed
@override
visitStaticInvocation(StaticInvocation node) {
if (node.target == _getPropertyTarget) {
node = _lowerGetProperty(node);
+ } else if (node.target == _setPropertyTarget) {
+ node = _lowerSetProperty(node);
}
node.transformChildren(this);
return node;
@@ -49,7 +72,72 @@
],
// TODO(rileyporter): Copy type from getProperty when it's generic.
types: [DynamicType()],
- )..fileOffset = node.arguments.fileOffset)
+ )..fileOffset = arguments.fileOffset)
..fileOffset = node.fileOffset;
}
+
+ /// Lowers the given js_util `setProperty` call to the foreign_helper JS call
+ /// when the additional validation checks in `setProperty` can be elided.
+ /// Lowers `setProperty(o, 'property', value) to
+ /// `JS('', '#.#=#', o, 'property', value)`.
+ StaticInvocation _lowerSetProperty(StaticInvocation node) {
+ Arguments arguments = node.arguments;
+ assert(arguments.types.isEmpty);
+ assert(arguments.positional.length == 3);
+ assert(arguments.named.isEmpty);
+
+ if (!_allowedInterop(arguments.positional.last)) {
+ return node;
+ }
+
+ return StaticInvocation(
+ _jsTarget,
+ Arguments([
+ StringLiteral(""),
+ StringLiteral("#.#=#"),
+ ...arguments.positional
+ ], types: [
+ DynamicType()
+ ])
+ ..fileOffset = arguments.fileOffset)
+ ..fileOffset = node.fileOffset;
+ }
+
+ /// Returns whether the given TreeNode is guaranteed to be allowed to interop
+ /// with JS.
+ ///
+ /// Returns true when the node is guaranteed to be not a function:
+ /// - has a DartType that is NullType or an InterfaceType that is not
+ /// Function or Object
+ /// Also returns true for allowed method calls within the JavaScript domain:
+ /// - dart:_foreign_helper JS
+ /// - dart:js allowInterop
+ /// - dart:js_util and any of the `_allowedInteropJsUtilMembers`
+ bool _allowedInterop(TreeNode node) {
+ // TODO(rileyporter): Detect functions that have been wrapped at some point
+ // with `allowInterop`.
+ // TODO(rileyporter): Use staticTypeContext to generalize type checking and
+ // allow more non-function types. Currently, we skip all literal types.
+ var checkType;
+ if (node is VariableGet) {
+ checkType = node.variable.type;
+ }
+
+ if (node is StaticInvocation) {
+ if (node.target == _allowInteropTarget) return true;
+ if (node.target == _jsTarget) return true;
+ if (_allowedInteropJsUtilTargets.contains(node.target)) return true;
+ checkType = node.target.function.returnType;
+ }
+
+ // TODO(rileyporter): Utilize Kernel code checking if checkType is related
+ // to FunctionType and could potentially be a function.
+ if (checkType is InterfaceType) {
+ return checkType.classNode.name != 'Function' &&
+ checkType.classNode.name != 'Object';
+ } else {
+ // Only other DartType guaranteed to not be a function.
+ return checkType is NullType;
+ }
+ }
}
diff --git a/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart b/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
index 2d9aa2c..ed1ea21 100644
--- a/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
+++ b/pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
@@ -80,7 +80,7 @@
return;
}
_instrumentationService.logRequest(data);
- final Map<String, Object> json = jsonDecode(data);
+ final Map<String, Object?> json = jsonDecode(data);
if (RequestMessage.canParse(json, nullLspJsonReporter)) {
onMessage(RequestMessage.fromJson(json));
} else if (NotificationMessage.canParse(json, nullLspJsonReporter)) {
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
index 25c012e..b2cb46f 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
@@ -1,8 +1,6 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// 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.
-//
-// @dart = 2.9
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
@@ -28,7 +26,7 @@
/// The analysis server that was created when a client established a
/// connection, or `null` if no such connection has yet been established.
@override
- LspAnalysisServer analysisServer;
+ LspAnalysisServer? analysisServer;
/// The function used to create a new SDK using the default SDK.
final DartSdkManager sdkManager;
diff --git a/pkg/analysis_server/lib/src/server/lsp_stdio_server.dart b/pkg/analysis_server/lib/src/server/lsp_stdio_server.dart
index e96bb9c..2fa5b8a 100644
--- a/pkg/analysis_server/lib/src/server/lsp_stdio_server.dart
+++ b/pkg/analysis_server/lib/src/server/lsp_stdio_server.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'dart:io';
import 'package:analysis_server/src/lsp/channel/lsp_byte_stream_channel.dart';
diff --git a/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart b/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart
index 94f3328..4889961 100644
--- a/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/analyzer_status_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -46,7 +44,7 @@
// To avoid races, set up listeners for the notifications before we initialise
// and track which event came first to ensure they arrived in the expected
// order.
- bool firstNotificationWasAnalyzing;
+ bool? firstNotificationWasAnalyzing;
final startNotification = waitForAnalysisStart()
.then((_) => firstNotificationWasAnalyzing ??= true);
final completeNotification = waitForAnalysisComplete()
diff --git a/pkg/analysis_server/test/integration/lsp_server/diagnostic_test.dart b/pkg/analysis_server/test/integration/lsp_server/diagnostic_test.dart
index 6b026f7..e9ed76a 100644
--- a/pkg/analysis_server/test/integration/lsp_server/diagnostic_test.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/diagnostic_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -29,7 +27,7 @@
final diagnosticsUpdate = waitForDiagnostics(mainFileUri);
await initialize();
- final diagnostics = await diagnosticsUpdate;
+ final diagnostics = (await diagnosticsUpdate)!;
expect(diagnostics, hasLength(1));
final diagnostic = diagnostics.first;
@@ -38,8 +36,9 @@
startsWith(
"Local variable 'x' can't be referenced before it is declared"));
- expect(diagnostic.relatedInformation, hasLength(1));
- final relatedInfo = diagnostic.relatedInformation.first;
+ final relatedInformation = diagnostic.relatedInformation!;
+ expect(relatedInformation, hasLength(1));
+ final relatedInfo = relatedInformation.first;
expect(relatedInfo.message, equals("The declaration of 'x' is here."));
expect(relatedInfo.location.uri, equals('$mainFileUri'));
expect(relatedInfo.location.range, equals(rangeFromMarkers(content)));
@@ -50,7 +49,7 @@
final diagnosticsUpdate = waitForDiagnostics(mainFileUri);
await initialize();
- final diagnostics = await diagnosticsUpdate;
+ final diagnostics = (await diagnosticsUpdate)!;
expect(diagnostics, hasLength(1));
final diagnostic = diagnostics.first;
expect(diagnostic.code, equals('invalid_assignment'));
@@ -70,7 +69,7 @@
final diagnosticsUpdate = waitForDiagnostics(mainFileUri);
await initialize();
- final diagnostics = await diagnosticsUpdate;
+ final diagnostics = (await diagnosticsUpdate)!;
expect(diagnostics, hasLength(1));
final diagnostic = diagnostics.first;
expect(diagnostic.code, equals('await_only_futures'));
diff --git a/pkg/analysis_server/test/integration/lsp_server/initialization_test.dart b/pkg/analysis_server/test/integration/lsp_server/initialization_test.dart
index 422086a..c1ba011 100644
--- a/pkg/analysis_server/test/integration/lsp_server/initialization_test.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/initialization_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
import 'package:test/test.dart';
@@ -29,8 +27,7 @@
);
final response = await sendRequestToServer(request);
expect(response.id, equals(request.id));
- expect(response.error, isNotNull);
- expect(response.error.code, equals(ErrorCodes.InvalidParams));
+ expect(response.error!.code, equals(ErrorCodes.InvalidParams));
expect(response.result, isNull);
}
}
diff --git a/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart b/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart
index fa871ff..66949c4 100644
--- a/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/integration_tests.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'dart:async';
import 'dart:io';
@@ -18,13 +16,13 @@
abstract class AbstractLspAnalysisServerIntegrationTest
with ClientCapabilitiesHelperMixin, LspAnalysisServerTestMixin {
final List<String> vmArgs = [];
- LspServerClient client;
- InstrumentationService instrumentationService;
-
- final Map<int, Completer<ResponseMessage>> _completers = {};
+ LspServerClient? client;
+ InstrumentationService? instrumentationService;
+ final Map<num, Completer<ResponseMessage>> _completers = {};
+ LspByteStreamServerChannel get channel => client!.channel!;
@override
- Stream<Message> get serverToClient => client.serverToClient;
+ Stream<Message> get serverToClient => client!.serverToClient;
/// Sends a request to the server and unwraps the result. Throws if the
/// response was not successful or returned an error.
@@ -32,25 +30,26 @@
Future<T> expectSuccessfulResponseTo<T, R>(
RequestMessage request, T Function(R) fromJson) async {
final resp = await sendRequestToServer(request);
- if (resp.error != null) {
- throw resp.error;
+ final error = resp.error;
+ if (error != null) {
+ throw error;
} else if (T == Null) {
return resp.result == null
- ? null
+ ? null as T
: throw 'Expected Null response but got ${resp.result}';
} else {
return fromJson(resp.result);
}
}
- void newFile(String path, {String content}) =>
+ void newFile(String path, {String? content}) =>
File(path).writeAsStringSync(content ?? '');
void newFolder(String path) => Directory(path).createSync(recursive: true);
@override
void sendNotificationToServer(NotificationMessage notification) =>
- client.channel.sendNotification(notification);
+ channel.sendNotification(notification);
@override
Future<ResponseMessage> sendRequestToServer(RequestMessage request) {
@@ -59,14 +58,14 @@
(string) => throw 'String IDs not supported in tests');
_completers[id] = completer;
- client.channel.sendRequest(request);
+ channel.sendRequest(request);
return completer.future;
}
@override
void sendResponseToServer(ResponseMessage response) =>
- client.channel.sendResponse(response);
+ channel.sendResponse(response);
@mustCallSuper
Future<void> setUp() async {
@@ -82,11 +81,12 @@
analysisOptionsPath = join(projectFolderPath, 'analysis_options.yaml');
analysisOptionsUri = Uri.file(analysisOptionsPath);
- client = LspServerClient(instrumentationService);
+ final client = LspServerClient(instrumentationService);
+ this.client = client;
await client.start(vmArgs: vmArgs);
client.serverToClient.listen((message) {
if (message is ResponseMessage) {
- final id = message.id.map((number) => number,
+ final id = message.id!.map((number) => number,
(string) => throw 'String IDs not supported in tests');
final completer = _completers[id];
@@ -102,26 +102,26 @@
void tearDown() {
// TODO(dantup): Graceful shutdown?
- client.close();
+ client?.close();
}
}
class LspServerClient {
- final InstrumentationService instrumentationService;
- Process _process;
- LspByteStreamServerChannel channel;
+ final InstrumentationService? instrumentationService;
+ Process? _process;
+ LspByteStreamServerChannel? channel;
final StreamController<Message> _serverToClient =
StreamController<Message>.broadcast();
LspServerClient(this.instrumentationService);
- Future<int> get exitCode => _process.exitCode;
+ Future<int> get exitCode => _process!.exitCode;
Stream<Message> get serverToClient => _serverToClient.stream;
void close() {
- channel.close();
- _process.kill();
+ channel?.close();
+ _process?.kill();
}
/// Find the root directory of the analysis_server package by proceeding
@@ -137,7 +137,7 @@
return dirname(pathname);
}
- Future start({List<String> vmArgs}) async {
+ Future start({List<String>? vmArgs}) async {
if (_process != null) {
throw Exception('Process already started');
}
@@ -164,8 +164,9 @@
}
final arguments = [...?vmArgs, serverPath, '--lsp', '--suppress-analytics'];
- _process = await Process.start(dartBinary, arguments);
- _process.exitCode.then((int code) {
+ final process = await Process.start(dartBinary, arguments);
+ _process = process;
+ process.exitCode.then((int code) {
if (code != 0) {
// TODO(dantup): Log/fail tests...
}
@@ -173,14 +174,14 @@
// If the server writes to stderr, fail tests with a more useful message
// (rather than having the test just hang waiting for a response).
- _process.stderr.listen((data) {
+ process.stderr.listen((data) {
final message = String.fromCharCodes(data);
throw 'Analysis Server wrote to stderr:\n\n$message';
});
- channel = LspByteStreamServerChannel(_process.stdout, _process.stdin,
- instrumentationService ?? InstrumentationService.NULL_SERVICE);
- channel.listen(_serverToClient.add);
+ channel = LspByteStreamServerChannel(process.stdout, process.stdin,
+ instrumentationService ?? InstrumentationService.NULL_SERVICE)
+ ..listen(_serverToClient.add);
}
}
diff --git a/pkg/analysis_server/test/integration/lsp_server/server_test.dart b/pkg/analysis_server/test/integration/lsp_server/server_test.dart
index 2771564..1e85c22 100644
--- a/pkg/analysis_server/test/integration/lsp_server/server_test.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/server_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'dart:convert';
import 'dart:io';
@@ -45,11 +43,11 @@
await sendShutdown();
sendExit();
- await client.channel.closed.timeout(const Duration(seconds: 10),
+ await channel.closed.timeout(const Duration(seconds: 10),
onTimeout: () =>
fail('Server channel did not close within 10 seconds'));
- final exitCode = await client.exitCode.timeout(const Duration(seconds: 10),
+ final exitCode = await client!.exitCode.timeout(const Duration(seconds: 10),
onTimeout: () => fail('Server process did not exit within 10 seconds'));
expect(exitCode, equals(0));
@@ -62,11 +60,11 @@
await initialize();
sendExit();
- await client.channel.closed.timeout(const Duration(seconds: 10),
+ await channel.closed.timeout(const Duration(seconds: 10),
onTimeout: () =>
fail('Server channel did not close within 10 seconds'));
- final exitCode = await client.exitCode.timeout(const Duration(seconds: 10),
+ final exitCode = await client!.exitCode.timeout(const Duration(seconds: 10),
onTimeout: () => fail('Server process did not exit within 10 seconds'));
expect(exitCode, equals(1));
@@ -76,11 +74,11 @@
await sendShutdown();
sendExit();
- await client.channel.closed.timeout(const Duration(seconds: 10),
+ await channel.closed.timeout(const Duration(seconds: 10),
onTimeout: () =>
fail('Server channel did not close within 10 seconds'));
- final exitCode = await client.exitCode.timeout(const Duration(seconds: 10),
+ final exitCode = await client!.exitCode.timeout(const Duration(seconds: 10),
onTimeout: () => fail('Server process did not exit within 10 seconds'));
expect(exitCode, equals(0));
@@ -93,8 +91,8 @@
sendExit();
- await client.channel.closed;
- final exitCode = await client.exitCode;
+ await channel.closed;
+ final exitCode = await client!.exitCode;
expect(exitCode, equals(1));
}
diff --git a/pkg/analysis_server/test/integration/lsp_server/test_all.dart b/pkg/analysis_server/test/integration/lsp_server/test_all.dart
index 2a5741e..3c85a81 100644
--- a/pkg/analysis_server/test/integration/lsp_server/test_all.dart
+++ b/pkg/analysis_server/test/integration/lsp_server/test_all.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'analyzer_status_test.dart' as analyzer_status;
diff --git a/pkg/analysis_server/test/lsp/analyzer_status_test.dart b/pkg/analysis_server/test/lsp/analyzer_status_test.dart
index 8d1492a..a137a95 100644
--- a/pkg/analysis_server/test/lsp/analyzer_status_test.dart
+++ b/pkg/analysis_server/test/lsp/analyzer_status_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -68,7 +66,7 @@
// To avoid races, set up listeners for the notifications before we initialise
// and track which event came first to ensure they arrived in the expected
// order.
- bool firstNotificationWasAnalyzing;
+ bool? firstNotificationWasAnalyzing;
final startNotification = waitForAnalysisStart()
.then((_) => firstNotificationWasAnalyzing ??= true);
final completeNotification = waitForAnalysisComplete()
diff --git a/pkg/analysis_server/test/lsp/cancel_request_test.dart b/pkg/analysis_server/test/lsp/cancel_request_test.dart
index 6780145..f39dff9 100644
--- a/pkg/analysis_server/test/lsp/cancel_request_test.dart
+++ b/pkg/analysis_server/test/lsp/cancel_request_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
diff --git a/pkg/analysis_server/test/lsp/change_workspace_folders_test.dart b/pkg/analysis_server/test/lsp/change_workspace_folders_test.dart
index 6d7431c..98ad357 100644
--- a/pkg/analysis_server/test/lsp/change_workspace_folders_test.dart
+++ b/pkg/analysis_server/test/lsp/change_workspace_folders_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -17,8 +15,8 @@
@reflectiveTest
class ChangeWorkspaceFoldersTest extends AbstractLspAnalysisServerTest {
- String workspaceFolder1Path, workspaceFolder2Path, workspaceFolder3Path;
- Uri workspaceFolder1Uri, workspaceFolder2Uri, workspaceFolder3Uri;
+ late String workspaceFolder1Path, workspaceFolder2Path, workspaceFolder3Path;
+ late Uri workspaceFolder1Uri, workspaceFolder2Uri, workspaceFolder3Uri;
@override
void setUp() {
diff --git a/pkg/analysis_server/test/lsp/closing_labels_test.dart b/pkg/analysis_server/test/lsp/closing_labels_test.dart
index 3a400e1..4a671a6 100644
--- a/pkg/analysis_server/test/lsp/closing_labels_test.dart
+++ b/pkg/analysis_server/test/lsp/closing_labels_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
diff --git a/pkg/analysis_server/test/lsp/code_actions_abstract.dart b/pkg/analysis_server/test/lsp/code_actions_abstract.dart
index ee4ca4d..591a70e 100644
--- a/pkg/analysis_server/test/lsp/code_actions_abstract.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_abstract.dart
@@ -2,10 +2,9 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:collection/collection.dart';
import 'package:test/test.dart';
import 'server_abstract.dart';
@@ -19,8 +18,7 @@
bool asCommand = false,
}) async {
final codeActions = await getCodeActions(uri.toString());
- final codeAction = findCommand(codeActions, command);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, command)!;
codeAction.map(
(command) {
@@ -34,17 +32,16 @@
if (!asCodeActionLiteral) {
throw 'Got CodeAction literal but expected Command';
}
- expect(codeAction, isNotNull);
expect(codeAction.title, equals(title));
- expect(codeAction.command.title, equals(title));
- expect(codeAction.command.arguments, equals([uri.toFilePath()]));
+ expect(codeAction.command!.title, equals(title));
+ expect(codeAction.command!.arguments, equals([uri.toFilePath()]));
},
);
}
- Either2<Command, CodeAction> findCommand(
+ Either2<Command, CodeAction>? findCommand(
List<Either2<Command, CodeAction>> actions, String commandID,
- [String wantedTitle]) {
+ [String? wantedTitle]) {
for (var codeAction in actions) {
final id = codeAction.map(
(cmd) => cmd.command, (action) => action.command?.command);
@@ -57,10 +54,9 @@
return null;
}
- CodeAction findEditAction(List<Either2<Command, CodeAction>> actions,
+ CodeAction? findEditAction(List<Either2<Command, CodeAction>> actions,
CodeActionKind actionKind, String title) {
- return findEditActions(actions, actionKind, title)
- .firstWhere((element) => true, orElse: () => null);
+ return findEditActions(actions, actionKind, title).firstOrNull;
}
List<CodeAction> findEditActions(List<Either2<Command, CodeAction>> actions,
@@ -69,22 +65,13 @@
.map((action) => action.map((cmd) => null, (action) => action))
.where((action) => action?.kind == actionKind && action?.title == title)
.map((action) {
- // Expect matching actions to contain an edit and not a command.
- assert(action.command == null);
- assert(action.edit != null);
- return action;
- }).toList();
- }
-
- Future<Either2<Command, CodeAction>> getFixAllAction(
- String title, Uri uri, String content) async {
- // TODO(dantup): Fix this once new server support has landed.
- throw UnimplementedError();
- // final codeActions =
- // await getCodeActions(uri.toString(), range: rangeFromMarkers(content));
- // final fixAction =
- // findCommand(codeActions, Commands.fixAllOfErrorCodeInFile, title);
- // return fixAction;
+ // Expect matching actions to contain an edit and not a command.
+ assert(action!.command == null);
+ assert(action!.edit != null);
+ return action;
+ })
+ .whereNotNull()
+ .toList();
}
/// Verifies that executing the given code actions command on the server
@@ -93,10 +80,10 @@
Future verifyCodeActionEdits(Either2<Command, CodeAction> codeAction,
String content, String expectedContent,
{bool expectDocumentChanges = false,
- Either2<num, String> workDoneToken}) async {
+ Either2<num, String>? workDoneToken}) async {
final command = codeAction.map(
(command) => command,
- (codeAction) => codeAction.command,
+ (codeAction) => codeAction.command!,
);
await verifyCommandEdits(command, content, expectedContent,
@@ -110,10 +97,10 @@
Future<void> verifyCommandEdits(
Command command, String content, String expectedContent,
{bool expectDocumentChanges = false,
- Either2<num, String> workDoneToken}) async {
- ApplyWorkspaceEditParams editParams;
+ Either2<num, String>? workDoneToken}) async {
+ ApplyWorkspaceEditParams? editParams;
- final commandResponse = await handleExpectedRequest<Object,
+ final commandResponse = await handleExpectedRequest<Object?,
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
Method.workspace_applyEdit,
ApplyWorkspaceEditParams.fromJson,
@@ -130,12 +117,13 @@
// Ensure the edit came back, and using the expected changes.
expect(editParams, isNotNull);
+ final edit = editParams!.edit;
if (expectDocumentChanges) {
- expect(editParams.edit.changes, isNull);
- expect(editParams.edit.documentChanges, isNotNull);
+ expect(edit.changes, isNull);
+ expect(edit.documentChanges, isNotNull);
} else {
- expect(editParams.edit.changes, isNotNull);
- expect(editParams.edit.documentChanges, isNull);
+ expect(edit.changes, isNotNull);
+ expect(edit.documentChanges, isNull);
}
// Ensure applying the changes will give us the expected content.
@@ -144,9 +132,9 @@
};
if (expectDocumentChanges) {
- applyDocumentChanges(contents, editParams.edit.documentChanges);
+ applyDocumentChanges(contents, edit.documentChanges!);
} else {
- applyChanges(contents, editParams.edit.changes);
+ applyChanges(contents, edit.changes!);
}
expect(contents[mainFilePath], equals(expectedContent));
}
diff --git a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
index 69e627b..07a3194 100644
--- a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
@@ -2,11 +2,10 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_custom_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:collection/collection.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -55,18 +54,18 @@
final assist = findEditAction(
codeActions,
CodeActionKind('refactor.add.showCombinator'),
- "Add explicit 'show' combinator");
+ "Add explicit 'show' combinator")!;
// Ensure the edit came back, and using documentChanges.
- expect(assist, isNotNull);
- expect(assist.edit.documentChanges, isNotNull);
- expect(assist.edit.changes, isNull);
+ final edit = assist.edit!;
+ expect(edit.documentChanges, isNotNull);
+ expect(edit.changes, isNull);
// Ensure applying the changes will give us the expected content.
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyDocumentChanges(contents, assist.edit.documentChanges);
+ applyDocumentChanges(contents, edit.documentChanges!);
expect(contents[mainFilePath], equals(expectedContent));
}
@@ -94,18 +93,18 @@
final assistAction = findEditAction(
codeActions,
CodeActionKind('refactor.add.showCombinator'),
- "Add explicit 'show' combinator");
+ "Add explicit 'show' combinator")!;
// Ensure the edit came back, and using changes.
- expect(assistAction, isNotNull);
- expect(assistAction.edit.changes, isNotNull);
- expect(assistAction.edit.documentChanges, isNull);
+ final edit = assistAction.edit!;
+ expect(edit.changes, isNotNull);
+ expect(edit.documentChanges, isNull);
// Ensure applying the changes will give us the expected content.
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyChanges(contents, assistAction.edit.changes);
+ applyChanges(contents, edit.changes!);
expect(contents[mainFilePath], equals(expectedContent));
}
@@ -180,24 +179,26 @@
final marker = positionFromMarker(content);
final codeActions = await getCodeActions(mainFileUri.toString(),
range: Range(start: marker, end: marker));
- final assist = findEditAction(codeActions,
- CodeActionKind('refactor.flutter.wrap.generic'), 'Wrap with widget...');
+ final assist = findEditAction(
+ codeActions,
+ CodeActionKind('refactor.flutter.wrap.generic'),
+ 'Wrap with widget...')!;
// Ensure the edit came back, and using documentChanges.
- expect(assist, isNotNull);
- expect(assist.edit.documentChanges, isNotNull);
- expect(assist.edit.changes, isNull);
+ final edit = assist.edit!;
+ expect(edit.documentChanges, isNotNull);
+ expect(edit.changes, isNull);
// Ensure applying the changes will give us the expected content.
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyDocumentChanges(contents, assist.edit.documentChanges);
+ applyDocumentChanges(contents, edit.documentChanges!);
expect(contents[mainFilePath], equals(expectedContent));
// Also ensure there was a single edit that was correctly marked
// as a SnippetTextEdit.
- final textEdits = _extractTextDocumentEdits(assist.edit.documentChanges)
+ final textEdits = _extractTextDocumentEdits(edit.documentChanges!)
.expand((tde) => tde.edits)
.map((edit) => edit.map(
(e) => e,
@@ -240,17 +241,18 @@
final marker = positionFromMarker(content);
final codeActions = await getCodeActions(mainFileUri.toString(),
range: Range(start: marker, end: marker));
- final assist = findEditAction(codeActions,
- CodeActionKind('refactor.flutter.wrap.generic'), 'Wrap with widget...');
+ final assist = findEditAction(
+ codeActions,
+ CodeActionKind('refactor.flutter.wrap.generic'),
+ 'Wrap with widget...')!;
// Ensure the edit came back, and using documentChanges.
- expect(assist, isNotNull);
- expect(assist.edit.documentChanges, isNotNull);
- expect(assist.edit.changes, isNull);
+ final edit = assist.edit!;
+ expect(edit.documentChanges, isNotNull);
+ expect(edit.changes, isNull);
// Extract just TextDocumentEdits, create/rename/delete are not relevant.
- final textDocumentEdits =
- _extractTextDocumentEdits(assist.edit.documentChanges);
+ final textDocumentEdits = _extractTextDocumentEdits(edit.documentChanges!);
final textEdits = textDocumentEdits
.expand((tde) => tde.edits)
.map((edit) => edit.map((e) => e, (e) => e, (e) => e))
@@ -284,7 +286,7 @@
(delete) => null,
),
)
- .where((e) => e != null)
+ .whereNotNull()
.toList(),
);
}
diff --git a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
index 8e6ba91..5b8f242 100644
--- a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:linter/src/rules.dart';
import 'package:path/path.dart' as path;
@@ -45,19 +43,21 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
- final fixAction = findEditAction(codeActions,
- CodeActionKind('quickfix.remove.unusedImport'), 'Remove unused import');
+ final fixAction = findEditAction(
+ codeActions,
+ CodeActionKind('quickfix.remove.unusedImport'),
+ 'Remove unused import')!;
// Ensure the edit came back, and using documentChanges.
- expect(fixAction, isNotNull);
- expect(fixAction.edit.documentChanges, isNotNull);
- expect(fixAction.edit.changes, isNull);
+ final edit = fixAction.edit!;
+ expect(edit.documentChanges, isNotNull);
+ expect(edit.changes, isNull);
// Ensure applying the changes will give us the expected content.
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyDocumentChanges(contents, fixAction.edit.documentChanges);
+ applyDocumentChanges(contents, edit.documentChanges!);
expect(contents[mainFilePath], equals(expectedContent));
}
@@ -83,19 +83,21 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
- final fixAction = findEditAction(codeActions,
- CodeActionKind('quickfix.remove.unusedImport'), 'Remove unused import');
+ final fixAction = findEditAction(
+ codeActions,
+ CodeActionKind('quickfix.remove.unusedImport'),
+ 'Remove unused import')!;
// Ensure the edit came back, and using changes.
- expect(fixAction, isNotNull);
- expect(fixAction.edit.changes, isNotNull);
- expect(fixAction.edit.documentChanges, isNull);
+ final edit = fixAction.edit!;
+ expect(edit.changes, isNotNull);
+ expect(edit.documentChanges, isNull);
// Ensure applying the changes will give us the expected content.
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyChanges(contents, fixAction.edit.changes);
+ applyChanges(contents, edit.changes!);
expect(contents[mainFilePath], equals(expectedContent));
}
@@ -118,16 +120,16 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final fixAction = findEditAction(codeActions,
- CodeActionKind('quickfix.create.file'), "Create file 'newfile.dart'");
+ CodeActionKind('quickfix.create.file'), "Create file 'newfile.dart'")!;
- expect(fixAction, isNotNull);
- expect(fixAction.edit.documentChanges, isNotNull);
+ final edit = fixAction.edit!;
+ expect(edit.documentChanges!, isNotNull);
// Ensure applying the changes creates the file and with the expected content.
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyDocumentChanges(contents, fixAction.edit.documentChanges);
+ applyDocumentChanges(contents, edit.documentChanges!);
expect(contents[expectedCreatedFile], isNotEmpty);
}
@@ -240,18 +242,18 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final fixAction = findEditAction(codeActions,
- CodeActionKind('quickfix.organize.imports'), 'Organize Imports');
+ CodeActionKind('quickfix.organize.imports'), 'Organize Imports')!;
// Ensure the edit came back, and using changes.
- expect(fixAction, isNotNull);
- expect(fixAction.edit.changes, isNotNull);
- expect(fixAction.edit.documentChanges, isNull);
+ final edit = fixAction.edit!;
+ expect(edit.changes, isNotNull);
+ expect(edit.documentChanges, isNull);
// Ensure applying the changes will give us the expected content.
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyChanges(contents, fixAction.edit.changes);
+ applyChanges(contents, edit.changes!);
expect(contents[mainFilePath], equals(expectedContent));
}
@@ -298,10 +300,7 @@
expect(fixTitle, equals("Create function 'foo'"));
}
- @failingTest
Future<void> test_fixAll_notWhenSingle() async {
- // TODO(dantup): Fix the text used to locate the fix once the
- // new server support has landed.
const content = '''
void f(String a) {
[[print(a!)]];
@@ -314,17 +313,16 @@
emptyTextDocumentClientCapabilities, [CodeActionKind.QuickFix]),
);
- final fixAction = await getFixAllAction(
- "Apply all: Remove the '!'", mainFileUri, content);
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ range: rangeFromMarkers(content));
+ final fixAction = findEditAction(
+ codeActions, CodeActionKind('quickfix'), "Remove '!'s in file");
// Should not appear if there was only a single error.
expect(fixAction, isNull);
}
- @failingTest
Future<void> test_fixAll_whenMultiple() async {
- // TODO(dantup): Fix this test up to use the new server support for
- // fix-all-in-file once landed.
const content = '''
void f(String a) {
[[print(a!!)]];
@@ -344,13 +342,17 @@
emptyTextDocumentClientCapabilities, [CodeActionKind.QuickFix]),
);
- final fixAction = await getFixAllAction(
- "Apply all: Remove the '!'", mainFileUri, content);
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ range: rangeFromMarkers(content));
+ final fixAction = findEditAction(
+ codeActions, CodeActionKind('quickfix'), "Remove '!'s in file")!;
- expect(fixAction, isNotNull);
-
- await verifyCodeActionEdits(
- fixAction, withoutMarkers(content), expectedContent);
+ // Ensure applying the changes will give us the expected content.
+ final contents = {
+ mainFilePath: withoutMarkers(content),
+ };
+ applyChanges(contents, fixAction.edit!.changes!);
+ expect(contents[mainFilePath], equals(expectedContent));
}
Future<void> test_noDuplicates_differentFix() async {
@@ -387,7 +389,7 @@
final secondBangPos =
positionFromOffset(withoutMarkers(content).indexOf('!);'), content);
expect(removeNnaAction.first.diagnostics, hasLength(1));
- final diagStart = removeNnaAction.first.diagnostics.first.range.start;
+ final diagStart = removeNnaAction.first.diagnostics!.first.range.start;
expect(diagStart, equals(secondBangPos));
}
}
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 1301230..3f8055f 100644
--- a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'dart:async';
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
@@ -75,8 +73,7 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
- expect(codeAction, isNotNull);
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
@@ -105,12 +102,11 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
- expect(codeAction, isNotNull);
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
// Respond to any applyEdit requests from the server with successful responses
// and capturing the last edit.
- WorkspaceEdit edit;
+ late WorkspaceEdit edit;
requestsFromServer.listen((request) async {
if (request.method == Method.workspace_applyEdit) {
final params = ApplyWorkspaceEditParams.fromJson(request.params);
@@ -132,7 +128,7 @@
final contents = {
mainFilePath: withoutMarkers(content),
};
- applyChanges(contents, edit.changes);
+ applyChanges(contents, edit.changes!);
expect(contents[mainFilePath], equals(expectedContent));
}
@@ -149,8 +145,7 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
- expect(codeAction, isNotNull);
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
// Send an edit request immediately after the refactor request.
final req1 = executeCodeAction(codeAction);
@@ -211,8 +206,7 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
- expect(codeAction, isNotNull);
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
@@ -262,7 +256,8 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
+
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent,
workDoneToken: clientProvidedTestWorkDoneToken);
@@ -296,7 +291,8 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
+
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
@@ -347,7 +343,8 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractMethodTitle);
+ findCommand(codeActions, Commands.performRefactor, extractMethodTitle)!;
+
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
@@ -382,8 +379,7 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction = findCommand(
- codeActions, Commands.performRefactor, extractVariableTitle);
- expect(codeAction, isNotNull);
+ codeActions, Commands.performRefactor, extractVariableTitle)!;
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
@@ -413,8 +409,7 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction = findCommand(
- codeActions, Commands.performRefactor, extractVariableTitle);
- expect(codeAction, isNotNull);
+ codeActions, Commands.performRefactor, extractVariableTitle)!;
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
@@ -494,8 +489,7 @@
final codeActions = await getCodeActions(mainFileUri.toString(),
range: rangeFromMarkers(content));
final codeAction =
- findCommand(codeActions, Commands.performRefactor, extractWidgetTitle);
- expect(codeAction, isNotNull);
+ findCommand(codeActions, Commands.performRefactor, extractWidgetTitle)!;
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
diff --git a/pkg/analysis_server/test/lsp/code_actions_source_test.dart b/pkg/analysis_server/test/lsp/code_actions_source_test.dart
index 0d0ca6e..6d977b8 100644
--- a/pkg/analysis_server/test/lsp/code_actions_source_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_source_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:test/test.dart';
@@ -43,8 +41,7 @@
withDocumentChangesSupport(emptyWorkspaceClientCapabilities)));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.organizeImports);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.organizeImports)!;
await verifyCodeActionEdits(codeAction, content, expectedContent,
expectDocumentChanges: true);
@@ -72,8 +69,7 @@
withApplyEditSupport(emptyWorkspaceClientCapabilities));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.organizeImports);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.organizeImports)!;
await verifyCodeActionEdits(codeAction, content, expectedContent);
}
@@ -116,12 +112,11 @@
withApplyEditSupport(emptyWorkspaceClientCapabilities));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.organizeImports);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.organizeImports)!;
final command = codeAction.map(
(command) => command,
- (codeAction) => codeAction.command,
+ (codeAction) => codeAction.command!,
);
final commandResponse = await executeCommand(command);
@@ -162,12 +157,11 @@
withApplyEditSupport(emptyWorkspaceClientCapabilities));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.organizeImports);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.organizeImports)!;
final command = codeAction.map(
(command) => command,
- (codeAction) => codeAction.command,
+ (codeAction) => codeAction.command!,
);
// Execute the command and it should return without needing us to process
@@ -217,8 +211,7 @@
withDocumentChangesSupport(emptyWorkspaceClientCapabilities)));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.sortMembers);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.sortMembers)!;
await verifyCodeActionEdits(codeAction, content, expectedContent,
expectDocumentChanges: true);
@@ -239,8 +232,7 @@
withApplyEditSupport(emptyWorkspaceClientCapabilities));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.sortMembers);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.sortMembers)!;
await verifyCodeActionEdits(codeAction, content, expectedContent);
}
@@ -286,15 +278,14 @@
withApplyEditSupport(emptyWorkspaceClientCapabilities));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.sortMembers);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.sortMembers)!;
final command = codeAction.map(
(command) => command,
- (codeAction) => codeAction.command,
+ (codeAction) => codeAction.command!,
);
- final commandResponse = handleExpectedRequest<Object,
+ final commandResponse = handleExpectedRequest<Object?,
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
Method.workspace_applyEdit,
ApplyWorkspaceEditParams.fromJson,
@@ -320,12 +311,11 @@
withApplyEditSupport(emptyWorkspaceClientCapabilities));
final codeActions = await getCodeActions(mainFileUri.toString());
- final codeAction = findCommand(codeActions, Commands.sortMembers);
- expect(codeAction, isNotNull);
+ final codeAction = findCommand(codeActions, Commands.sortMembers)!;
final command = codeAction.map(
(command) => command,
- (codeAction) => codeAction.command,
+ (codeAction) => codeAction.command!,
);
// Ensure the request returned an error (error repsonses are thrown by
diff --git a/pkg/analysis_server/test/lsp/completion.dart b/pkg/analysis_server/test/lsp/completion.dart
index 7d8dd01..75c85b1 100644
--- a/pkg/analysis_server/test/lsp/completion.dart
+++ b/pkg/analysis_server/test/lsp/completion.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:test/test.dart';
@@ -13,14 +11,14 @@
int sortTextSorter(CompletionItem item1, CompletionItem item2) =>
(item1.sortText ?? item1.label).compareTo(item2.sortText ?? item2.label);
- Future<String> verifyCompletions(
+ Future<String?> verifyCompletions(
Uri fileUri,
String content, {
- List<String> expectCompletions,
- String applyEditsFor,
+ required List<String> expectCompletions,
+ String? applyEditsFor,
bool resolve = false,
- String expectedContent,
- String expectedContentIfInserting,
+ String? expectedContent,
+ String? expectedContentIfInserting,
bool verifyInsertReplaceRanges = false,
bool openCloseFile = true,
}) async {
@@ -71,7 +69,7 @@
// Replacing.
updatedContent = applyTextEdits(
withoutMarkers(content),
- [textEditForReplace(item.textEdit)],
+ [textEditForReplace(item.textEdit!)],
);
expect(
withCaret(updatedContent, insertFormat), equals(expectedContent));
@@ -79,14 +77,14 @@
// Inserting.
final inserted = applyTextEdits(
withoutMarkers(content),
- [textEditForInsert(item.textEdit)],
+ [textEditForInsert(item.textEdit!)],
);
expect(withCaret(inserted, insertFormat),
equals(expectedContentIfInserting));
} else {
updatedContent = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(item.textEdit)],
+ [toTextEdit(item.textEdit!)],
);
if (expectedContent != null) {
expect(
@@ -101,7 +99,7 @@
/// Replaces the LSP snippet placeholder '${0:}' with '^' for easier verifying
/// of the cursor position in completions.
- String withCaret(String contents, InsertTextFormat format) =>
+ String withCaret(String contents, InsertTextFormat? format) =>
format == InsertTextFormat.Snippet
? contents.replaceFirst(r'${0:}', '^')
: contents;
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index faacf84..920891e 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -2,12 +2,11 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:collection/collection.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -27,10 +26,8 @@
with CompletionTestMixin {
void expectAutoImportCompletion(List<CompletionItem> items, String file) {
expect(
- items.singleWhere(
- (c) => c.detail?.contains("Auto import from '$file'") ?? false,
- orElse: () => null,
- ),
+ items.singleWhereOrNull(
+ (c) => c.detail?.contains("Auto import from '$file'") ?? false),
isNotNull,
);
}
@@ -166,7 +163,7 @@
// placeholders.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b})'));
- final textEdit = toTextEdit(item.textEdit);
+ final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals(item.insertText));
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -213,7 +210,7 @@
// placeholders.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
expect(item.insertText, equals('setState(() {\n \${0:}\n \\});'));
- final textEdit = toTextEdit(item.textEdit);
+ final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals(item.insertText));
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -242,7 +239,7 @@
// With no required params, there should still be parens and a tabstop inside.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
expect(item.insertText, equals(r'myFunction(${0:})'));
- final textEdit = toTextEdit(item.textEdit);
+ final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals(item.insertText));
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -268,7 +265,7 @@
// no need for snippets.
expect(item.insertTextFormat, isNull);
expect(item.insertText, equals(r'min'));
- final textEdit = toTextEdit(item.textEdit);
+ final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals(item.insertText));
}
@@ -302,7 +299,7 @@
// Ensure the item can be resolved and gets a proper TextEdit.
final resolved = await resolveCompletion(item);
expect(resolved.textEdit, isNotNull);
- final textEdit = toTextEdit(resolved.textEdit);
+ final textEdit = toTextEdit(resolved.textEdit!);
expect(textEdit.newText, equals(item.insertText));
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -488,10 +485,9 @@
await initialize();
await openFile(mainFileUri, withoutMarkers(content));
final res = await getCompletion(mainFileUri, positionFromMarker(content));
- final item = res.singleWhere((c) => c.label.startsWith('name =>'),
- orElse: () => null);
+ final item = res.singleWhereOrNull((c) => c.label.startsWith('name =>'));
expect(item, isNotNull);
- expect(item.label, equals('name => …'));
+ expect(item!.label, equals('name => …'));
expect(item.filterText, isNull); // Falls back to label
expect(item.insertText, equals('''@override
// TODO: implement name
@@ -608,8 +604,8 @@
final res = await getCompletion(mainFileUri, positionFromMarker(content));
final fromServer = res.singleWhere((c) => c.label == 'x');
- final fromPlugin = res.singleWhere((c) => c.label == 'x.toUpperCase()',
- orElse: () => null);
+ final fromPlugin =
+ res.singleWhereOrNull((c) => c.label == 'x.toUpperCase()');
// Server results should still be included.
expect(fromServer.kind, equals(CompletionItemKind.Variable));
@@ -670,14 +666,14 @@
// we expect.
final replaced = applyTextEdits(
withoutMarkers(content),
- [textEditForReplace(item.textEdit)],
+ [textEditForReplace(item.textEdit!)],
);
expect(replaced, contains('a.abcdefghij\n'));
// When using the insert range, we should retain what was after the caret
// ("def" in this case).
final inserted = applyTextEdits(
withoutMarkers(content),
- [textEditForInsert(item.textEdit)],
+ [textEditForInsert(item.textEdit!)],
);
expect(inserted, contains('a.abcdefghijdef\n'));
}
@@ -758,7 +754,7 @@
expect(item.deprecated, isNull);
// If the does not say it supports the deprecated flag, we should show
// '(deprecated)' in the details.
- expect(item.detail.toLowerCase(), contains('deprecated'));
+ expect(item.detail!.toLowerCase(), contains('deprecated'));
}
Future<void> test_isDeprecated_supportedFlag() async {
@@ -817,8 +813,8 @@
Future<void> check(
String code,
String expectedLabel, {
- String expectedReplace,
- String expectedInsert,
+ required String expectedReplace,
+ required String expectedInsert,
}) async {
final content = '''
class A { const A({int argOne, int argTwo, String argThree}); }
@@ -943,7 +939,7 @@
expect(item.insertText, anyOf(equals('test'), isNull));
final updated = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(item.textEdit)],
+ [toTextEdit(item.textEdit!)],
);
expect(updated, contains('one: '));
}
@@ -967,7 +963,7 @@
// need to be provided.
expect(item.insertTextFormat, isNull);
expect(item.insertText, isNull);
- final textEdit = toTextEdit(item.textEdit);
+ final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals('one: '));
expect(
textEdit.range,
@@ -999,7 +995,7 @@
// placeholder.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
expect(item.insertText, equals(r'one: ${0:},'));
- final textEdit = toTextEdit(item.textEdit);
+ final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals(r'one: ${0:},'));
expect(
textEdit.range,
@@ -1058,7 +1054,7 @@
expect(item.insertText, anyOf(equals('abcdefghij'), isNull));
final updated = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(item.textEdit)],
+ [toTextEdit(item.textEdit!)],
);
expect(updated, contains('a.abcdefghij'));
}
@@ -1167,7 +1163,7 @@
// Ensure the doc comment was added.
expect(
- resolved.documentation.valueEquals('This class is in another file.'),
+ resolved.documentation!.valueEquals('This class is in another file.'),
isTrue,
);
@@ -1181,8 +1177,8 @@
// Apply both the main completion edit and the additionalTextEdits atomically.
final newContent = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(resolved.textEdit)]
- .followedBy(resolved.additionalTextEdits)
+ [toTextEdit(resolved.textEdit!)]
+ .followedBy(resolved.additionalTextEdits!)
.toList(),
);
@@ -1287,8 +1283,8 @@
// Apply both the main completion edit and the additionalTextEdits atomically.
final newContent = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(resolved.textEdit)]
- .followedBy(resolved.additionalTextEdits)
+ [toTextEdit(resolved.textEdit!)]
+ .followedBy(resolved.additionalTextEdits!)
.toList(),
);
@@ -1483,7 +1479,7 @@
// Ensure the doc comment was added.
expect(
- resolved.documentation.valueEquals('This class is in another file.'),
+ resolved.documentation!.valueEquals('This class is in another file.'),
isTrue,
);
@@ -1499,14 +1495,14 @@
final newContentReplaceMode = applyTextEdits(
withoutMarkers(content),
- [textEditForReplace(resolved.textEdit)]
- .followedBy(resolved.additionalTextEdits)
+ [textEditForReplace(resolved.textEdit!)]
+ .followedBy(resolved.additionalTextEdits!)
.toList(),
);
final newContentInsertMode = applyTextEdits(
withoutMarkers(content),
- [textEditForInsert(resolved.textEdit)]
- .followedBy(resolved.additionalTextEdits)
+ [textEditForInsert(resolved.textEdit!)]
+ .followedBy(resolved.additionalTextEdits!)
.toList(),
);
@@ -1573,8 +1569,8 @@
// Apply all current-document edits.
final newContent = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(resolved.textEdit)]
- .followedBy(resolved.additionalTextEdits)
+ [toTextEdit(resolved.textEdit!)]
+ .followedBy(resolved.additionalTextEdits!)
.toList(),
);
expect(newContent, equals('''
@@ -1585,12 +1581,12 @@
'''));
// Execute the associated command (which will handle edits in other files).
- ApplyWorkspaceEditParams editParams;
- final commandResponse = await handleExpectedRequest<Object,
+ ApplyWorkspaceEditParams? editParams;
+ final commandResponse = await handleExpectedRequest<Object?,
ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse>(
Method.workspace_applyEdit,
ApplyWorkspaceEditParams.fromJson,
- () => executeCommand(resolved.command),
+ () => executeCommand(resolved.command!),
handler: (edit) {
// When the server sends the edit back, just keep a copy and say we
// applied successfully (it'll be verified below).
@@ -1603,13 +1599,13 @@
// Ensure the edit came back.
expect(editParams, isNotNull);
- expect(editParams.edit.changes, isNotNull);
+ expect(editParams!.edit.changes, isNotNull);
// Ensure applying the changes will give us the expected content.
final contents = {
parentFilePath: withoutMarkers(parentContent),
};
- applyChanges(contents, editParams.edit.changes);
+ applyChanges(contents, editParams!.edit.changes!);
// Check the parent file was modified to include the import by the edits
// that came from the server.
@@ -1677,8 +1673,8 @@
// Apply both the main completion edit and the additionalTextEdits atomically.
final newContent = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(resolved.textEdit)]
- .followedBy(resolved.additionalTextEdits)
+ [toTextEdit(resolved.textEdit!)]
+ .followedBy(resolved.additionalTextEdits!)
.toList(),
);
@@ -1771,8 +1767,8 @@
// Apply both the main completion edit and the additionalTextEdits atomically.
final newContent = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(resolved.textEdit)]
- .followedBy(resolved.additionalTextEdits)
+ [toTextEdit(resolved.textEdit!)]
+ .followedBy(resolved.additionalTextEdits!)
.toList(),
);
@@ -1811,10 +1807,7 @@
// Ensure the item doesn't appear in the results (because we might not
// be able to execute the import edits if they're in another file).
- final completion = res.singleWhere(
- (c) => c.label == 'InOtherFile',
- orElse: () => null,
- );
+ final completion = res.singleWhereOrNull((c) => c.label == 'InOtherFile');
expect(completion, isNull);
}
@@ -1840,10 +1833,7 @@
// Ensure the item doesn't appear in the results (because we might not
// be able to execute the import edits if they're in another file).
- final completion = res.singleWhere(
- (c) => c.label == 'InOtherFile',
- orElse: () => null,
- );
+ final completion = res.singleWhereOrNull((c) => c.label == 'InOtherFile');
expect(completion, isNull);
}
@@ -1869,7 +1859,7 @@
expect(item.insertText, anyOf(equals('abcdefghij'), isNull));
final updated = applyTextEdits(
withoutMarkers(content),
- [toTextEdit(item.textEdit)],
+ [toTextEdit(item.textEdit!)],
);
expect(updated, contains('a.abcdefghij'));
}
@@ -1920,7 +1910,7 @@
// placeholders.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, c: ${3:c})'));
- final textEdit = toTextEdit(item.textEdit);
+ final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals(item.insertText));
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -1962,7 +1952,7 @@
// Ensure the item can be resolved and gets a proper TextEdit.
final resolved = await resolveCompletion(item);
expect(resolved.textEdit, isNotNull);
- final textEdit = toTextEdit(resolved.textEdit);
+ final textEdit = toTextEdit(resolved.textEdit!);
expect(textEdit.newText, equals(item.insertText));
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
diff --git a/pkg/analysis_server/test/lsp/completion_yaml_test.dart b/pkg/analysis_server/test/lsp/completion_yaml_test.dart
index 878d367..9d26755 100644
--- a/pkg/analysis_server/test/lsp/completion_yaml_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_yaml_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/src/services/pub/pub_api.dart';
import 'package:http/http.dart';
import 'package:linter/src/rules.dart';
@@ -107,7 +105,7 @@
@reflectiveTest
class FixDataCompletionTest extends AbstractLspAnalysisServerTest
with CompletionTestMixin {
- Uri fixDataUri;
+ late Uri fixDataUri;
@override
void setUp() {
@@ -332,7 +330,7 @@
'one: ',
);
expect(
- completion.documentation.valueEquals('Description of package'),
+ completion.documentation!.valueEquals('Description of package'),
isTrue,
);
}
@@ -399,14 +397,14 @@
// Versions are currently only available if we've previously resolved on the
// package name, so first complete/resolve that.
- final newContent = await verifyCompletions(
+ final newContent = (await verifyCompletions(
pubspecFileUri,
content,
expectCompletions: ['one: '],
resolve: true,
applyEditsFor: 'one: ',
openCloseFile: false,
- );
+ ))!;
await replaceFile(222, pubspecFileUri, newContent);
await verifyCompletions(
diff --git a/pkg/analysis_server/test/lsp/configuration_test.dart b/pkg/analysis_server/test/lsp/configuration_test.dart
index bf57ef4..a1bb699 100644
--- a/pkg/analysis_server/test/lsp/configuration_test.dart
+++ b/pkg/analysis_server/test/lsp/configuration_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
diff --git a/pkg/analysis_server/test/lsp/definition_test.dart b/pkg/analysis_server/test/lsp/definition_test.dart
index 08b013a..f15c592 100644
--- a/pkg/analysis_server/test/lsp/definition_test.dart
+++ b/pkg/analysis_server/test/lsp/definition_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart' as lsp;
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart
index 0d066fe..73b227a 100644
--- a/pkg/analysis_server/test/lsp/diagnostic_test.dart
+++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart
@@ -2,10 +2,7 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
-import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:test/test.dart';
@@ -21,8 +18,6 @@
@reflectiveTest
class DiagnosticTest extends AbstractLspAnalysisServerTest {
- Folder pedanticLibFolder;
-
Future<void> checkPluginErrorsForFile(String pluginAnalyzedFilePath) async {
final pluginAnalyzedUri = Uri.file(pluginAnalyzedFilePath);
@@ -50,7 +45,7 @@
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final err = diagnostics.first;
+ final err = diagnostics!.first;
expect(err.severity, DiagnosticSeverity.Error);
expect(err.message, equals('Test error from plugin'));
expect(err.code, equals('ERR1'));
@@ -60,7 +55,7 @@
expect(err.range.end.character, equals(6));
expect(err.relatedInformation, hasLength(1));
- final related = err.relatedInformation[0];
+ final related = err.relatedInformation![0];
expect(related.message, equals('Related error'));
expect(related.location.range.start.line, equals(1));
expect(related.location.range.start.character, equals(12));
@@ -96,7 +91,7 @@
await initialize();
final initialDiagnostics = await firstDiagnosticsUpdate;
expect(initialDiagnostics, hasLength(1));
- expect(initialDiagnostics.first.severity, DiagnosticSeverity.Warning);
+ expect(initialDiagnostics!.first.severity, DiagnosticSeverity.Warning);
expect(initialDiagnostics.first.code, 'undefined_lint_warning');
}
@@ -111,7 +106,7 @@
await initialize();
final initialDiagnostics = await firstDiagnosticsUpdate;
expect(initialDiagnostics, hasLength(1));
- expect(initialDiagnostics.first.severity, DiagnosticSeverity.Warning);
+ expect(initialDiagnostics!.first.severity, DiagnosticSeverity.Warning);
expect(initialDiagnostics.first.code, 'include_file_not_found');
// TODO(scheglov) The server does not handle the file change.
@@ -139,7 +134,7 @@
await initialize();
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final diagnostic = diagnostics.first;
+ final diagnostic = diagnostics!.first;
expect(diagnostic.relatedInformation, hasLength(1));
}
@@ -154,7 +149,7 @@
await initialize();
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final diagnostic = diagnostics.first;
+ final diagnostic = diagnostics!.first;
expect(diagnostic.message, contains('\nTry'));
}
@@ -187,7 +182,7 @@
emptyTextDocumentClientCapabilities, [DiagnosticTag.Deprecated]));
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final diagnostic = diagnostics.first;
+ final diagnostic = diagnostics!.first;
expect(diagnostic.code, equals('deprecated_member_use_from_same_package'));
expect(diagnostic.tags, contains(DiagnosticTag.Deprecated));
}
@@ -204,7 +199,7 @@
await initialize();
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final diagnostic = diagnostics.first;
+ final diagnostic = diagnostics!.first;
expect(diagnostic.code, equals('deprecated_member_use_from_same_package'));
expect(diagnostic.tags, isNull);
}
@@ -223,7 +218,7 @@
emptyTextDocumentClientCapabilities, [DiagnosticTag.Unnecessary]));
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final diagnostic = diagnostics.first;
+ final diagnostic = diagnostics!.first;
expect(diagnostic.code, equals('dead_code'));
expect(diagnostic.tags, contains(DiagnosticTag.Unnecessary));
}
@@ -235,7 +230,7 @@
newFile(dotFolderFilePath, content: 'String a = 1;');
- List<Diagnostic> diagnostics;
+ List<Diagnostic>? diagnostics;
waitForDiagnostics(dotFolderFileUri).then((d) => diagnostics = d);
// Send a request for a hover.
@@ -258,7 +253,7 @@
await initialize();
final initialDiagnostics = await firstDiagnosticsUpdate;
expect(initialDiagnostics, hasLength(1));
- expect(initialDiagnostics.first.severity, DiagnosticSeverity.Error);
+ expect(initialDiagnostics!.first.severity, DiagnosticSeverity.Error);
expect(initialDiagnostics.first.code, 'invalid_value');
}
@@ -281,7 +276,7 @@
await initialize();
final initialDiagnostics = await initialDiagnosticsFuture;
expect(initialDiagnostics, hasLength(1));
- expect(initialDiagnostics.first.message, contains(serverErrorMessage));
+ expect(initialDiagnostics!.first.message, contains(serverErrorMessage));
final pluginTriggeredDiagnosticFuture = waitForDiagnostics(mainFileUri);
final pluginError = plugin.AnalysisError(
@@ -297,7 +292,7 @@
final pluginTriggeredDiagnostics = await pluginTriggeredDiagnosticFuture;
expect(
- pluginTriggeredDiagnostics.map((error) => error.message),
+ pluginTriggeredDiagnostics!.map((error) => error.message),
containsAll([
pluginErrorMessage,
contains(serverErrorMessage),
@@ -315,7 +310,7 @@
await initialize();
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final diagnostic = diagnostics.first;
+ final diagnostic = diagnostics!.first;
expect(diagnostic.code, equals('invalid_assignment'));
expect(diagnostic.range.start.line, equals(0));
expect(diagnostic.range.start.character, equals(11));
@@ -332,7 +327,7 @@
await openFile(mainFileUri, 'final a = Bad();');
final diagnostics = await diagnosticsUpdate;
expect(diagnostics, hasLength(1));
- final diagnostic = diagnostics.first;
+ final diagnostic = diagnostics!.first;
expect(diagnostic.message, contains("The function 'Bad' isn't defined"));
}
diff --git a/pkg/analysis_server/test/lsp/document_changes_test.dart b/pkg/analysis_server/test/lsp/document_changes_test.dart
index 361ee23..ebdb22b 100644
--- a/pkg/analysis_server/test/lsp/document_changes_test.dart
+++ b/pkg/analysis_server/test/lsp/document_changes_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'dart:async';
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
@@ -54,7 +52,7 @@
)),
]);
- final notifiedChanges = pluginManager.analysisUpdateContentParams
+ final notifiedChanges = pluginManager.analysisUpdateContentParams!
.files[mainFilePath] as ChangeContentOverlay;
expect(
@@ -98,7 +96,7 @@
await _initializeAndOpen();
await closeFile(mainFileUri);
- expect(pluginManager.analysisUpdateContentParams.files,
+ expect(pluginManager.analysisUpdateContentParams!.files,
equals({mainFilePath: RemoveContentOverlay()}));
}
@@ -113,8 +111,8 @@
await openFile(Uri.file(fileOutsideRootPath), content);
// Expect both files return the same driver
- final driverForInside = server.getAnalysisDriver(fileInsideRootPath);
- final driverForOutside = server.getAnalysisDriver(fileOutsideRootPath);
+ final driverForInside = server.getAnalysisDriver(fileInsideRootPath)!;
+ final driverForOutside = server.getAnalysisDriver(fileOutsideRootPath)!;
expect(driverForInside, equals(driverForOutside));
// But that only the file inside the root was added.
expect(driverForInside.addedFiles, contains(fileInsideRootPath));
@@ -132,7 +130,7 @@
Future<void> test_documentOpen_notifiesPlugins() async {
await _initializeAndOpen();
- expect(pluginManager.analysisUpdateContentParams.files,
+ expect(pluginManager.analysisUpdateContentParams!.files,
equals({mainFilePath: AddContentOverlay(content)}));
}
@@ -167,7 +165,7 @@
await pumpEventQueue(times: 5000);
// Ensure the opened file is in the priority list.
- expect(server.getAnalysisDriver(mainFilePath).priorityFiles,
+ expect(server.getAnalysisDriver(mainFilePath)!.priorityFiles,
equals([mainFilePath]));
}
diff --git a/pkg/analysis_server/test/lsp/document_highlights_test.dart b/pkg/analysis_server/test/lsp/document_highlights_test.dart
index 1812f86..68e1200 100644
--- a/pkg/analysis_server/test/lsp/document_highlights_test.dart
+++ b/pkg/analysis_server/test/lsp/document_highlights_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:test/test.dart';
@@ -120,7 +118,7 @@
if (expectedRanges.isEmpty) {
expect(highlights, isNull);
} else {
- final highlightRanges = highlights.map((h) => h.range).toList();
+ final highlightRanges = highlights!.map((h) => h.range).toList();
expect(highlightRanges, equals(expectedRanges));
}
}
diff --git a/pkg/analysis_server/test/lsp/document_symbols_test.dart b/pkg/analysis_server/test/lsp/document_symbols_test.dart
index 1883728..a37882a 100644
--- a/pkg/analysis_server/test/lsp/document_symbols_test.dart
+++ b/pkg/analysis_server/test/lsp/document_symbols_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:test/test.dart';
@@ -167,15 +165,15 @@
expect(myClass.kind, equals(SymbolKind.Class));
expect(myClass.children, hasLength(3));
- final field = myClass.children[0];
+ final field = myClass.children![0];
expect(field.name, equals('myField'));
expect(field.kind, equals(SymbolKind.Field));
- final constructor = myClass.children[1];
+ final constructor = myClass.children![1];
expect(constructor.name, equals('MyClass'));
expect(constructor.kind, equals(SymbolKind.Constructor));
- final method = myClass.children[2];
+ final method = myClass.children![2];
expect(method.name, equals('myMethod'));
expect(method.kind, equals(SymbolKind.Method));
}
diff --git a/pkg/analysis_server/test/lsp/file_modification_test.dart b/pkg/analysis_server/test/lsp/file_modification_test.dart
index 5f5e747..17ecd10 100644
--- a/pkg/analysis_server/test/lsp/file_modification_test.dart
+++ b/pkg/analysis_server/test/lsp/file_modification_test.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
import 'package:test/test.dart';
@@ -145,7 +143,7 @@
);
}
- String _getOverlay(String path) {
+ String? _getOverlay(String path) {
if (server.resourceProvider.hasOverlay(path)) {
return server.resourceProvider.getFile(path).readAsStringSync();
}
diff --git a/pkg/analysis_server/test/lsp/test_all.dart b/pkg/analysis_server/test/lsp/test_all.dart
index 4972696..dd27eb3 100644
--- a/pkg/analysis_server/test/lsp/test_all.dart
+++ b/pkg/analysis_server/test/lsp/test_all.dart
@@ -2,8 +2,6 @@
// 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.
-// @dart = 2.9
-
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../src/lsp/lsp_packet_transformer_test.dart' as lsp_packet_transformer;
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 96cd1ad..48edc3d 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -110,6 +110,7 @@
'dart:_interceptors',
'dart:_js_helper',
'dart:_late_helper',
+ 'dart:js',
'dart:js_util'
];
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index e374a78..3db7549 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 1.7.7-dev
+- Fixed issue where two clients subscribing to the same stream in close succession
+ could result in DDS sending multiple `streamListen` requests to the VM service.
+
# 1.7.6
- Update dependencies.
diff --git a/pkg/dds/lib/src/stream_manager.dart b/pkg/dds/lib/src/stream_manager.dart
index 26bc6d7..c39ed6a 100644
--- a/pkg/dds/lib/src/stream_manager.dart
+++ b/pkg/dds/lib/src/stream_manager.dart
@@ -137,6 +137,11 @@
) async {
assert(stream != null && stream.isNotEmpty);
if (!streamListeners.containsKey(stream)) {
+ // Initialize the list of clients for the new stream before we do
+ // anything else to ensure multiple clients registering for the same
+ // stream in quick succession doesn't result in multiple streamListen
+ // requests being sent to the VM service.
+ streamListeners[stream] = <DartDevelopmentServiceClient>[];
if ((stream == kDebugStream && client == null) ||
stream != kDebugStream) {
// This will return an RPC exception if the stream doesn't exist. This
@@ -146,7 +151,6 @@
});
assert(result['type'] == 'Success');
}
- streamListeners[stream] = <DartDevelopmentServiceClient>[];
}
if (streamListeners[stream].contains(client)) {
throw kStreamAlreadySubscribedException;
diff --git a/pkg/dds/test/regress_45569_test.dart b/pkg/dds/test/regress_45569_test.dart
new file mode 100644
index 0000000..e5afaf1
--- /dev/null
+++ b/pkg/dds/test/regress_45569_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+// @dart=2.10
+
+import 'dart:io';
+
+import 'package:dds/dds.dart';
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service_io.dart';
+import 'common/test_helper.dart';
+
+void main() {
+ Process process;
+ DartDevelopmentService dds;
+
+ setUp(() async {
+ // We don't care what's actually running in the target process for this
+ // test, so we're just using an existing one.
+ process = await spawnDartProcess(
+ 'get_stream_history_script.dart',
+ pauseOnStart: false,
+ );
+ });
+
+ tearDown(() async {
+ await dds?.shutdown();
+ process?.kill();
+ dds = null;
+ process = null;
+ });
+
+ test('Ensure streamListen and streamCancel calls are handled atomically',
+ () async {
+ dds = await DartDevelopmentService.startDartDevelopmentService(
+ remoteVmServiceUri,
+ );
+ expect(dds.isRunning, true);
+ final connection1 = await vmServiceConnectUri(dds.wsUri.toString());
+ final connection2 = await vmServiceConnectUri(dds.wsUri.toString());
+
+ for (int i = 0; i < 50; ++i) {
+ final listenFutures = <Future>[
+ connection1.streamListen('Service'),
+ connection2.streamListen('Service'),
+ ];
+ await Future.wait(listenFutures);
+
+ final cancelFutures = <Future>[
+ connection1.streamCancel('Service'),
+ connection2.streamCancel('Service'),
+ ];
+ await Future.wait(cancelFutures);
+ }
+ });
+}
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index c585aa4..cb84d2a 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -93,6 +93,7 @@
'dart:collection',
'dart:html',
'dart:indexed_db',
+ 'dart:js',
'dart:js_util',
'dart:math',
'dart:svg',
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index a3f002c..a06ae74 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -165,6 +165,7 @@
reload_sources_test: SkipByDesign # Hot reload is disabled in AOT mode.
rewind_optimized_out_test: SkipByDesign # Debugger is disabled in AOT mode.
rewind_test: SkipByDesign # Debugger is disabled in AOT mode.
+sdk_break_with_mixin_test: SkipByDesign # Debugger is disabled in AOT mode.
set_breakpoint_state_test: SkipByDesign # Debugger is disabled in AOT mode.
set_library_debuggable_test: SkipByDesign # Debugger is disabled in AOT mode.
sigquit_starts_service_test: SkipByDesign # Spawns a secondary process using Platform.executable.
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index a3f002c..a06ae74 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -165,6 +165,7 @@
reload_sources_test: SkipByDesign # Hot reload is disabled in AOT mode.
rewind_optimized_out_test: SkipByDesign # Debugger is disabled in AOT mode.
rewind_test: SkipByDesign # Debugger is disabled in AOT mode.
+sdk_break_with_mixin_test: SkipByDesign # Debugger is disabled in AOT mode.
set_breakpoint_state_test: SkipByDesign # Debugger is disabled in AOT mode.
set_library_debuggable_test: SkipByDesign # Debugger is disabled in AOT mode.
sigquit_starts_service_test: SkipByDesign # Spawns a secondary process using Platform.executable.
diff --git a/sdk/lib/js_util/js_util.dart b/sdk/lib/js_util/js_util.dart
index df75900..bec43ab 100644
--- a/sdk/lib/js_util/js_util.dart
+++ b/sdk/lib/js_util/js_util.dart
@@ -68,9 +68,13 @@
bool hasProperty(Object o, Object name) => JS('bool', '# in #', name, o);
+// All usage optimized away in a CFE transformation. Changes here will not
+// affect the generated JS.
dynamic getProperty(Object o, Object name) =>
JS('Object|Null', '#[#]', o, name);
+// Some usage optimized away in a CFE transformation. Changes here might not
+// affect the generated JS.
dynamic setProperty(Object o, Object name, Object? value) {
assertInterop(value);
return JS('', '#[#]=#', o, name, value);
diff --git a/tests/lib/js/js_util/properties_test.dart b/tests/lib/js/js_util/properties_test.dart
index ab0b844..9da37ff 100644
--- a/tests/lib/js/js_util/properties_test.dart
+++ b/tests/lib/js/js_util/properties_test.dart
@@ -252,6 +252,8 @@
js_util.setProperty(f, 'a', 100);
expect(f.a, equals(100));
expect(js_util.getProperty(f, 'a'), equals(100));
+ js_util.setProperty(f, 'a', null);
+ expect(f.a, equals(null));
expect(js_util.getProperty(f, 'list') is List, isTrue);
js_util.setProperty(f, 'list', [8]);
@@ -260,6 +262,23 @@
js_util.setProperty(f, 'newProperty', 'new');
expect(js_util.getProperty(f, 'newProperty'), equals('new'));
+
+ // Using a variable for the property value.
+ var num = 4;
+ js_util.setProperty(f, 'a', num);
+ expect(f.a, equals(num));
+ var str = 'bar';
+ js_util.setProperty(f, 'a', str);
+ expect(f.a, equals(str));
+ var b = false;
+ js_util.setProperty(f, 'a', b);
+ expect(f.a, equals(b));
+ var list = [2, 4, 6];
+ js_util.setProperty(f, 'a', list);
+ expect(f.a, equals(list));
+ var fn = allowInterop(dartFunction);
+ js_util.setProperty(f, 'a', fn);
+ expect(f.a, equals(fn));
});
test('typed literal', () {
@@ -314,6 +333,13 @@
String bar = _getBarWithSideEffect();
js_util.setProperty(f, bar, 'baz');
expect(js_util.getProperty(f, bar), equals('baz'));
+ js_util.setProperty(f, _getBarWithSideEffect(), 'mumble');
+ expect(js_util.getProperty(f, bar), equals('mumble'));
+
+ // Set property to a function call.
+ js_util.setProperty(f, 'a', dartFunction());
+ String expected = dartFunction();
+ expect(f.a, equals(expected));
});
});
diff --git a/tests/lib_2/js/js_util/properties_test.dart b/tests/lib_2/js/js_util/properties_test.dart
index ab0b844..9da37ff 100644
--- a/tests/lib_2/js/js_util/properties_test.dart
+++ b/tests/lib_2/js/js_util/properties_test.dart
@@ -252,6 +252,8 @@
js_util.setProperty(f, 'a', 100);
expect(f.a, equals(100));
expect(js_util.getProperty(f, 'a'), equals(100));
+ js_util.setProperty(f, 'a', null);
+ expect(f.a, equals(null));
expect(js_util.getProperty(f, 'list') is List, isTrue);
js_util.setProperty(f, 'list', [8]);
@@ -260,6 +262,23 @@
js_util.setProperty(f, 'newProperty', 'new');
expect(js_util.getProperty(f, 'newProperty'), equals('new'));
+
+ // Using a variable for the property value.
+ var num = 4;
+ js_util.setProperty(f, 'a', num);
+ expect(f.a, equals(num));
+ var str = 'bar';
+ js_util.setProperty(f, 'a', str);
+ expect(f.a, equals(str));
+ var b = false;
+ js_util.setProperty(f, 'a', b);
+ expect(f.a, equals(b));
+ var list = [2, 4, 6];
+ js_util.setProperty(f, 'a', list);
+ expect(f.a, equals(list));
+ var fn = allowInterop(dartFunction);
+ js_util.setProperty(f, 'a', fn);
+ expect(f.a, equals(fn));
});
test('typed literal', () {
@@ -314,6 +333,13 @@
String bar = _getBarWithSideEffect();
js_util.setProperty(f, bar, 'baz');
expect(js_util.getProperty(f, bar), equals('baz'));
+ js_util.setProperty(f, _getBarWithSideEffect(), 'mumble');
+ expect(js_util.getProperty(f, bar), equals('mumble'));
+
+ // Set property to a function call.
+ js_util.setProperty(f, 'a', dartFunction());
+ String expected = dartFunction();
+ expect(f.a, equals(expected));
});
});
diff --git a/tools/VERSION b/tools/VERSION
index e394531..cf03173 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 25
+PRERELEASE 26
PRERELEASE_PATCH 0
\ No newline at end of file