Version 2.14.0-369.0.dev
Merge commit '2a99af9bc93e8fec4f4746168803fdeaff239c7b' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 8ccb4bf..9f7152c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -4,12 +4,11 @@
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
-import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
-import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
+import 'package:analysis_server/src/plugin/plugin_manager.dart';
import 'package:analysis_server/src/protocol_server.dart' hide Position;
import 'package:analysis_server/src/services/correction/assist.dart';
import 'package:analysis_server/src/services/correction/assist_internal.dart';
@@ -22,13 +21,36 @@
import 'package:analyzer/dart/analysis/session.dart'
show InconsistentAnalysisException;
import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
+import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:collection/collection.dart' show groupBy;
class CodeActionHandler extends MessageHandler<CodeActionParams,
List<Either2<Command, CodeAction>>> {
+ // Because server+plugin results are different types and we lose
+ // priorites when converting them to CodeActions, store the priorities
+ // against each action in an expando. This avoids wrapping CodeActions in
+ // another wrapper class (since we can't modify the LSP-spec-generated
+ // CodeAction class).
+ final codeActionPriorities = Expando<int>();
+
+ /// A comparator that can be used to sort [CodeActions]s using priorties
+ /// in [codeActionPriorities]. The highest number priority will be sorted
+ /// before lower number priorityies. Items with the same relevance are sorted
+ /// alphabetically by their title.
+ late final Comparator<CodeAction> _codeActionComparator =
+ (CodeAction a, CodeAction b) {
+ // We should never be sorting actions without priorities.
+ final aPriority = codeActionPriorities[a] ?? 0;
+ final bPriority = codeActionPriorities[b] ?? 0;
+ if (aPriority != bPriority) {
+ return bPriority - aPriority;
+ }
+ return a.title.compareTo(b.title);
+ };
+
CodeActionHandler(LspAnalysisServer server) : super(server);
@override
@@ -147,12 +169,12 @@
/// version of each document being modified so it's important to call this
/// immediately after computing edits to ensure the document is not modified
/// before the version number is read.
- CodeAction _createAssistAction(Assist assist) {
+ CodeAction _createAssistAction(SourceChange change) {
return CodeAction(
- title: assist.change.message,
- kind: toCodeActionKind(assist.change.id, CodeActionKind.Refactor),
+ title: change.message,
+ kind: toCodeActionKind(change.id, CodeActionKind.Refactor),
diagnostics: const [],
- edit: createWorkspaceEdit(server, assist.change),
+ edit: createWorkspaceEdit(server, change),
);
}
@@ -160,12 +182,12 @@
/// version of each document being modified so it's important to call this
/// immediately after computing edits to ensure the document is not modified
/// before the version number is read.
- CodeAction _createFixAction(Fix fix, Diagnostic diagnostic) {
+ CodeAction _createFixAction(SourceChange change, Diagnostic diagnostic) {
return CodeAction(
- title: fix.change.message,
- kind: toCodeActionKind(fix.change.id, CodeActionKind.QuickFix),
+ title: change.message,
+ kind: toCodeActionKind(change.id, CodeActionKind.QuickFix),
diagnostics: [diagnostic],
- edit: createWorkspaceEdit(server, fix.change),
+ edit: createWorkspaceEdit(server, change),
);
}
@@ -222,6 +244,7 @@
Future<List<Either2<Command, CodeAction>>> _getAssistActions(
bool Function(CodeActionKind?) shouldIncludeKind,
bool supportsLiteralCodeActions,
+ String path,
Range range,
int offset,
int length,
@@ -236,13 +259,28 @@
length,
);
final processor = AssistProcessor(context);
- final assists = await processor.compute();
- assists.sort(Assist.SORT_BY_RELEVANCE);
+ final serverFuture = processor.compute();
+ final pluginFuture = _getPluginAssistChanges(path, offset, length);
- final assistActions =
- _dedupeActions(assists.map(_createAssistAction), range.start);
+ final assists = await serverFuture;
+ final pluginChanges = await pluginFuture;
- return assistActions
+ final codeActions = <CodeAction>[];
+ codeActions.addAll(assists.map((assist) {
+ final action = _createAssistAction(assist.change);
+ codeActionPriorities[action] = assist.kind.priority;
+ return action;
+ }));
+ codeActions.addAll(pluginChanges.map((change) {
+ final action = _createAssistAction(change.change);
+ codeActionPriorities[action] = change.priority;
+ return action;
+ }));
+
+ final dedupedCodeActions = _dedupeActions(codeActions, range.start);
+ dedupedCodeActions.sort(_codeActionComparator);
+
+ return dedupedCodeActions
.where((action) => shouldIncludeKind(action.kind))
.map((action) => Either2<Command, CodeAction>.t2(action))
.toList();
@@ -268,11 +306,11 @@
final results = await Future.wait([
_getSourceActions(shouldIncludeKind, supportsLiterals,
supportsWorkspaceApplyEdit, path),
- _getAssistActions(
- shouldIncludeKind, supportsLiterals, range, offset, length, unit),
+ _getAssistActions(shouldIncludeKind, supportsLiterals, path, range,
+ offset, length, unit),
_getRefactorActions(
shouldIncludeKind, supportsLiterals, path, offset, length, unit),
- _getFixActions(shouldIncludeKind, supportsLiterals,
+ _getFixActions(shouldIncludeKind, supportsLiterals, path, offset,
supportedDiagnosticTags, range, unit),
]);
final flatResults = results.expand((x) => x).toList();
@@ -283,22 +321,23 @@
Future<List<Either2<Command, CodeAction>>> _getFixActions(
bool Function(CodeActionKind?) shouldIncludeKind,
bool supportsLiteralCodeActions,
+ String path,
+ int offset,
Set<DiagnosticTag> supportedDiagnosticTags,
Range range,
ResolvedUnitResult unit,
) async {
+ final clientSupportsCodeDescription =
+ server.clientCapabilities?.diagnosticCodeDescription ?? false;
+ // TODO(dantup): We may be missing fixes for pubspec, analysis_options,
+ // android manifests (see _computeServerErrorFixes in EditDomainHandler).
final lineInfo = unit.lineInfo;
final codeActions = <CodeAction>[];
final fixContributor = DartFixContributor();
- try {
- final errorCodeCounts = <ErrorCode, int>{};
- // Count the errors by code so we know whether to include a fix-all.
- for (final error in unit.errors) {
- errorCodeCounts[error.errorCode] =
- (errorCodeCounts[error.errorCode] ?? 0) + 1;
- }
+ final pluginFuture = _getPluginFixActions(unit, offset);
+ try {
for (final error in unit.errors) {
// Server lineNumber is one-based so subtract one.
var errorLine = lineInfo.getLocation(error.offset).lineNumber - 1;
@@ -317,22 +356,44 @@
}, extensionCache: server.getExtensionCacheFor(unit));
final fixes = await fixContributor.computeFixes(context);
if (fixes.isNotEmpty) {
- fixes.sort(Fix.SORT_BY_RELEVANCE);
-
final diagnostic = toDiagnostic(
unit,
error,
supportedTags: supportedDiagnosticTags,
- clientSupportsCodeDescription:
- server.clientCapabilities?.diagnosticCodeDescription ?? false,
+ clientSupportsCodeDescription: clientSupportsCodeDescription,
);
codeActions.addAll(
- fixes.map((fix) => _createFixAction(fix, diagnostic)),
+ fixes.map((fix) {
+ final action = _createFixAction(fix.change, diagnostic);
+ codeActionPriorities[action] = fix.kind.priority;
+ return action;
+ }),
);
}
}
+ Diagnostic pluginErrorToDiagnostic(AnalysisError error) {
+ return pluginToDiagnostic(
+ (_) => lineInfo,
+ error,
+ supportedTags: supportedDiagnosticTags,
+ clientSupportsCodeDescription: clientSupportsCodeDescription,
+ );
+ }
+
+ final pluginFixes = await pluginFuture;
+ final pluginFixActions = pluginFixes.expand(
+ (fix) => fix.fixes.map((fixChange) {
+ final action = _createFixAction(
+ fixChange.change, pluginErrorToDiagnostic(fix.error));
+ codeActionPriorities[action] = fixChange.priority;
+ return action;
+ }),
+ );
+ codeActions.addAll(pluginFixActions);
+
final dedupedActions = _dedupeActions(codeActions, range.start);
+ dedupedActions.sort(_codeActionComparator);
return dedupedActions
.where((action) => shouldIncludeKind(action.kind))
@@ -346,6 +407,61 @@
}
}
+ Future<Iterable<plugin.PrioritizedSourceChange>> _getPluginAssistChanges(
+ String path, int offset, int length) async {
+ final requestParams = plugin.EditGetAssistsParams(path, offset, length);
+ final driver = server.getAnalysisDriver(path);
+
+ Map<PluginInfo, Future<plugin.Response>> pluginFutures;
+ if (driver == null) {
+ pluginFutures = <PluginInfo, Future<plugin.Response>>{};
+ } else {
+ pluginFutures = server.pluginManager.broadcastRequest(
+ requestParams,
+ contextRoot: driver.analysisContext!.contextRoot,
+ );
+ }
+
+ final pluginChanges = <plugin.PrioritizedSourceChange>[];
+ final responses =
+ await waitForResponses(pluginFutures, requestParameters: requestParams);
+
+ for (final response in responses) {
+ final result = plugin.EditGetAssistsResult.fromResponse(response);
+ pluginChanges.addAll(result.assists);
+ }
+
+ return pluginChanges;
+ }
+
+ Future<Iterable<plugin.AnalysisErrorFixes>> _getPluginFixActions(
+ ResolvedUnitResult unit, int offset) async {
+ final file = unit.path;
+ final requestParams = plugin.EditGetFixesParams(file, offset);
+ final driver = server.getAnalysisDriver(file);
+
+ Map<PluginInfo, Future<plugin.Response>> pluginFutures;
+ if (driver == null) {
+ pluginFutures = <PluginInfo, Future<plugin.Response>>{};
+ } else {
+ pluginFutures = server.pluginManager.broadcastRequest(
+ requestParams,
+ contextRoot: driver.analysisContext!.contextRoot,
+ );
+ }
+
+ final pluginFixes = <plugin.AnalysisErrorFixes>[];
+ final responses =
+ await waitForResponses(pluginFutures, requestParameters: requestParams);
+
+ for (final response in responses) {
+ final result = plugin.EditGetFixesResult.fromResponse(response);
+ pluginFixes.addAll(result.fixes);
+ }
+
+ return pluginFixes;
+ }
+
Future<List<Either2<Command, CodeAction>>> _getRefactorActions(
bool Function(CodeActionKind) shouldIncludeKind,
bool supportsLiteralCodeActions,
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 429920f..03be64d 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -866,19 +866,30 @@
range: toRange(lineInfo, label.offset, label.length),
label: label.label);
-lsp.CodeActionKind toCodeActionKind(String? id, lsp.CodeActionKind fallback) {
+/// Converts [id] to a [CodeActionKind] using [fallbackOrPrefix] as a fallback
+/// or a prefix if the ID is not already a fix/refactor.
+lsp.CodeActionKind toCodeActionKind(
+ String? id, lsp.CodeActionKind fallbackOrPrefix) {
if (id == null) {
- return fallback;
+ return fallbackOrPrefix;
}
// Dart fixes and assists start with "dart.assist." and "dart.fix." but in LSP
// we want to use the predefined prefixes for CodeActions.
- final newId = id
+ var newId = id
.replaceAll('dart.assist', lsp.CodeActionKind.Refactor.toString())
.replaceAll('dart.fix', lsp.CodeActionKind.QuickFix.toString())
.replaceAll(
'analysisOptions.assist', lsp.CodeActionKind.Refactor.toString())
.replaceAll(
'analysisOptions.fix', lsp.CodeActionKind.QuickFix.toString());
+
+ // If the ID does not start with either of the kinds above, prefix it as
+ // it will be an unqualified ID from a plugin.
+ if (!newId.startsWith(lsp.CodeActionKind.Refactor.toString()) &&
+ !newId.startsWith(lsp.CodeActionKind.QuickFix.toString())) {
+ newId = '$fallbackOrPrefix.$newId';
+ }
+
return lsp.CodeActionKind(newId);
}
diff --git a/pkg/analysis_server/lib/src/utilities/mocks.dart b/pkg/analysis_server/lib/src/utilities/mocks.dart
index 5022dcd..9c581cd 100644
--- a/pkg/analysis_server/lib/src/utilities/mocks.dart
+++ b/pkg/analysis_server/lib/src/utilities/mocks.dart
@@ -146,6 +146,8 @@
plugin.AnalysisUpdateContentParams? analysisUpdateContentParams;
plugin.RequestParams? broadcastedRequest;
Map<PluginInfo, Future<plugin.Response>>? broadcastResults;
+ Map<PluginInfo, Future<plugin.Response>>? Function(plugin.RequestParams)?
+ handleRequest;
@override
List<PluginInfo> plugins = [];
@@ -192,7 +194,9 @@
plugin.RequestParams params,
{analyzer.ContextRoot? contextRoot}) {
broadcastedRequest = params;
- return broadcastResults ?? <PluginInfo, Future<plugin.Response>>{};
+ return handleRequest?.call(params) ??
+ broadcastResults ??
+ <PluginInfo, Future<plugin.Response>>{};
}
@override
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 67323f9..2797109 100644
--- a/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_assists_test.dart
@@ -7,6 +7,8 @@
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: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';
@@ -166,6 +168,87 @@
expect(codeActions, isEmpty);
}
+ Future<void> test_plugin() async {
+ // This code should get an assist to replace 'foo' with 'bar'.'
+ const content = '[[foo]]';
+ const expectedContent = 'bar';
+
+ final pluginResult = plugin.EditGetAssistsResult([
+ plugin.PrioritizedSourceChange(
+ 0,
+ plugin.SourceChange(
+ "Change 'foo' to 'bar'",
+ edits: [
+ plugin.SourceFileEdit(mainFilePath, 0,
+ edits: [plugin.SourceEdit(0, 3, 'bar')])
+ ],
+ id: 'fooToBar',
+ ),
+ )
+ ]);
+ configureTestPlugin(
+ handler: (request) =>
+ request is plugin.EditGetAssistsParams ? pluginResult : null,
+ );
+
+ newFile(mainFilePath, content: withoutMarkers(content));
+ await initialize(
+ textDocumentCapabilities: withCodeActionKinds(
+ emptyTextDocumentClientCapabilities, [CodeActionKind.Refactor]),
+ );
+
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ range: rangeFromMarkers(content));
+ final assist = findEditAction(codeActions,
+ CodeActionKind('refactor.fooToBar'), "Change 'foo' to 'bar'")!;
+
+ final edit = assist.edit!;
+ expect(edit.changes, isNotNull);
+
+ // Ensure applying the changes will give us the expected content.
+ final contents = {
+ mainFilePath: withoutMarkers(content),
+ };
+ applyChanges(contents, edit.changes!);
+ expect(contents[mainFilePath], equals(expectedContent));
+ }
+
+ Future<void> test_plugin_sortsWithServer() async {
+ // Produces a server assist of "Convert to single quoted string" (with a
+ // priority of 30).
+ const content = 'import "[[dart:async]]";';
+
+ // Provide two plugin results that should sort either side of the server assist.
+ final pluginResult = plugin.EditGetAssistsResult([
+ plugin.PrioritizedSourceChange(10, plugin.SourceChange('Low')),
+ plugin.PrioritizedSourceChange(100, plugin.SourceChange('High')),
+ ]);
+ configureTestPlugin(
+ handler: (request) =>
+ request is plugin.EditGetAssistsParams ? pluginResult : null,
+ );
+
+ newFile(mainFilePath, content: withoutMarkers(content));
+ await initialize(
+ textDocumentCapabilities: withCodeActionKinds(
+ emptyTextDocumentClientCapabilities, [CodeActionKind.Refactor]),
+ );
+
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ range: rangeFromMarkers(content));
+ final codeActionTitles = codeActions.map((action) =>
+ action.map((command) => command.title, (action) => action.title));
+
+ expect(
+ codeActionTitles,
+ containsAllInOrder([
+ 'High',
+ 'Convert to single quoted string',
+ 'Low',
+ ]),
+ );
+ }
+
Future<void> test_snippetTextEdits_supported() async {
// This tests experimental support for including Snippets in TextEdits.
// https://github.com/rust-analyzer/rust-analyzer/blob/b35559a2460e7f0b2b79a7029db0c5d4e0acdb44/docs/dev/lsp-extensions.md#snippet-textedit
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 2a9184a..925b13b 100644
--- a/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_fixes_test.dart
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
+import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:linter/src/rules.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
@@ -350,6 +352,111 @@
final codeActions = await getCodeActions(otherFileUri.toString());
expect(codeActions, isEmpty);
}
+
+ Future<void> test_plugin() async {
+ // This code should get a fix to replace 'foo' with 'bar'.'
+ const content = '[[foo]]';
+ const expectedContent = 'bar';
+
+ final pluginResult = plugin.EditGetFixesResult([
+ plugin.AnalysisErrorFixes(
+ plugin.AnalysisError(
+ plugin.AnalysisErrorSeverity.ERROR,
+ plugin.AnalysisErrorType.HINT,
+ plugin.Location(mainFilePath, 0, 3, 0, 0),
+ "Do not use 'foo'",
+ 'do_not_use_foo',
+ ),
+ fixes: [
+ plugin.PrioritizedSourceChange(
+ 0,
+ plugin.SourceChange(
+ "Change 'foo' to 'bar'",
+ edits: [
+ plugin.SourceFileEdit(mainFilePath, 0,
+ edits: [plugin.SourceEdit(0, 3, 'bar')])
+ ],
+ id: 'fooToBar',
+ ),
+ )
+ ],
+ )
+ ]);
+ configureTestPlugin(
+ handler: (request) =>
+ request is plugin.EditGetFixesParams ? pluginResult : null,
+ );
+
+ newFile(mainFilePath, content: withoutMarkers(content));
+ await initialize(
+ textDocumentCapabilities: withCodeActionKinds(
+ emptyTextDocumentClientCapabilities, [CodeActionKind.QuickFix]),
+ );
+
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ range: rangeFromMarkers(content));
+ final assist = findEditAction(codeActions,
+ CodeActionKind('quickfix.fooToBar'), "Change 'foo' to 'bar'")!;
+
+ final edit = assist.edit!;
+ expect(edit.changes, isNotNull);
+
+ // Ensure applying the changes will give us the expected content.
+ final contents = {
+ mainFilePath: withoutMarkers(content),
+ };
+ applyChanges(contents, edit.changes!);
+ expect(contents[mainFilePath], equals(expectedContent));
+ }
+
+ Future<void> test_plugin_sortsWithServer() async {
+ // Produces a server fix for removing unused import with a default
+ // priority of 50.
+ const content = '''
+[[import]] 'dart:convert';
+''';
+
+ // Provide two plugin results that should sort either side of the server fix.
+ final pluginResult = plugin.EditGetFixesResult([
+ plugin.AnalysisErrorFixes(
+ plugin.AnalysisError(
+ plugin.AnalysisErrorSeverity.ERROR,
+ plugin.AnalysisErrorType.HINT,
+ plugin.Location(mainFilePath, 0, 3, 0, 0),
+ 'Dummy error',
+ 'dummy',
+ ),
+ fixes: [
+ plugin.PrioritizedSourceChange(10, plugin.SourceChange('Low')),
+ plugin.PrioritizedSourceChange(100, plugin.SourceChange('High')),
+ ],
+ )
+ ]);
+ configureTestPlugin(
+ handler: (request) =>
+ request is plugin.EditGetFixesParams ? pluginResult : null,
+ );
+
+ newFile(mainFilePath, content: withoutMarkers(content));
+ await initialize(
+ textDocumentCapabilities: withCodeActionKinds(
+ emptyTextDocumentClientCapabilities, [CodeActionKind.QuickFix]),
+ );
+
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ range: rangeFromMarkers(content));
+ final codeActionTitles = codeActions.map((action) =>
+ action.map((command) => command.title, (action) => action.title));
+
+ expect(
+ codeActionTitles,
+ containsAllInOrder([
+ 'High',
+ 'Remove unused import',
+ 'Low',
+ ]),
+ );
+ }
}
@reflectiveTest
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index a7a8ea0..963542a 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -62,12 +62,25 @@
DiscoveredPluginInfo configureTestPlugin({
plugin.ResponseResult? respondWith,
plugin.Notification? notification,
+ plugin.ResponseResult? Function(plugin.RequestParams)? handler,
Duration respondAfter = Duration.zero,
}) {
final info = DiscoveredPluginInfo('a', 'b', 'c', server.notificationManager,
server.instrumentationService);
pluginManager.plugins.add(info);
+ if (handler != null) {
+ pluginManager.handleRequest = (request) {
+ final response = handler(request);
+ return response == null
+ ? null
+ : <PluginInfo, Future<plugin.Response>>{
+ info: Future.delayed(respondAfter)
+ .then((_) => response.toResponse('-', 1))
+ };
+ };
+ }
+
if (respondWith != null) {
pluginManager.broadcastResults = <PluginInfo, Future<plugin.Response>>{
info: Future.delayed(respondAfter)
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index 5376013..bac9b69 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -94,8 +94,8 @@
| textDocument/codeAction (organiseImports) | ✅ | ✅ | | ✅ | ✅ |
| textDocument/codeAction (fixAll) | ✅ | ✅ | | ✅ | ✅ |
| textDocument/codeAction (refactors) | ✅ | ✅ | | ✅ | ✅ |
-| textDocument/codeAction (assists) | ✅ | ✅ | | ✅ | ✅ | Only if the client advertises `codeActionLiteralSupport` with `Refactor`
-| textDocument/codeAction (fixes) | ✅ | ✅ | | ✅ | ✅ | Only if the client advertises `codeActionLiteralSupport` with `QuickFix`
+| textDocument/codeAction (assists) | ✅ | ✅ | ✅ | ✅ | ✅ | Only if the client advertises `codeActionLiteralSupport` with `Refactor`
+| textDocument/codeAction (fixes) | ✅ | ✅ | ✅ | ✅ | ✅ | Only if the client advertises `codeActionLiteralSupport` with `QuickFix`
| textDocument/codeLens | | | | | |
| codeLens/resolve | | | | | |
| textDocument/documentLink | | | | | |
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 1e448ff..aec3305 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -12,7 +12,6 @@
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/testing_data.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
-import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/constant/compute.dart';
import 'package:analyzer/src/dart/constant/constant_verifier.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
@@ -332,7 +331,6 @@
var enableTiming = _analysisOptions.enableTiming;
var nodeRegistry = NodeLintRegistry(enableTiming);
- var visitors = <AstVisitor>[];
var context = LinterContextImpl(
allUnits,
@@ -354,13 +352,6 @@
// Run lints that handle specific node types.
unit.accept(LinterVisitor(nodeRegistry));
-
- // Run visitor based lints.
- if (visitors.isNotEmpty) {
- AstVisitor visitor = ExceptionHandlingDelegatingAstVisitor(
- visitors, ExceptionHandlingDelegatingAstVisitor.logException);
- unit.accept(visitor);
- }
}
void _computeVerifyErrors(FileState file, CompilationUnit unit) {
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index 605628f..a068ad7 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -1302,38 +1302,13 @@
}
}
-/// A [DelegatingAstVisitor] that will additionally catch all exceptions from
-/// the delegates without stopping the visiting. A function must be provided
-/// that will be invoked for each such exception.
+/// Class capable of handling exceptions generated during linting.
///
/// Clients may not extend, implement or mix-in this class.
-class ExceptionHandlingDelegatingAstVisitor<T> extends DelegatingAstVisitor<T> {
- /// The function that will be executed for each exception that is thrown by
- /// one of the visit methods on the delegate.
- final ExceptionInDelegateHandler handler;
-
- /// Initialize a newly created visitor to use each of the given delegate
- /// visitors to visit the nodes of an AST structure.
- ExceptionHandlingDelegatingAstVisitor(
- Iterable<AstVisitor<T>> delegates, this.handler)
- : super(delegates);
-
- @override
- T? visitNode(AstNode node) {
- delegates.forEach((delegate) {
- try {
- node.accept(delegate);
- } catch (exception, stackTrace) {
- handler(node, delegate, exception, stackTrace);
- }
- });
- node.visitChildren(this);
- return null;
- }
-
- /// A function that can be used with instances of this class to log and then
- /// ignore any exceptions that are thrown by any of the delegates.
- static void logException(
+class LinterExceptionHandler {
+ /// A method that can be passed to the `LinterVisitor` constructor to handle
+ /// exceptions that occur during linting.
+ void logException(
AstNode node, Object visitor, dynamic exception, StackTrace stackTrace) {
StringBuffer buffer = StringBuffer();
buffer.write('Exception while using a ${visitor.runtimeType} to visit a ');
diff --git a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
index cebd705..0b1635c 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_analyzer.dart
@@ -332,7 +332,6 @@
ErrorReporter errorReporter = _getErrorReporter(file);
var nodeRegistry = NodeLintRegistry(_analysisOptions.enableTiming);
- var visitors = <AstVisitor>[];
final workspacePackage = _getPackage(currentUnit.unit);
var context = LinterContextImpl(
allUnits,
@@ -350,13 +349,6 @@
// Run lints that handle specific node types.
unit.accept(LinterVisitor(nodeRegistry));
-
- // Run visitor based lints.
- if (visitors.isNotEmpty) {
- AstVisitor visitor = ExceptionHandlingDelegatingAstVisitor(
- visitors, ExceptionHandlingDelegatingAstVisitor.logException);
- unit.accept(visitor);
- }
}
void _computeVerifyErrors(FileState file, CompilationUnit unit) {
diff --git a/pkg/analyzer/lib/src/lint/linter_visitor.dart b/pkg/analyzer/lib/src/lint/linter_visitor.dart
index 05926b0..25afec4 100644
--- a/pkg/analyzer/lib/src/lint/linter_visitor.dart
+++ b/pkg/analyzer/lib/src/lint/linter_visitor.dart
@@ -17,9 +17,9 @@
final NodeLintRegistry registry;
final LintRuleExceptionHandler exceptionHandler;
- LinterVisitor(this.registry,
- [this.exceptionHandler =
- ExceptionHandlingDelegatingAstVisitor.logException]);
+ LinterVisitor(this.registry, [LintRuleExceptionHandler? exceptionHandler])
+ : exceptionHandler =
+ exceptionHandler ?? LinterExceptionHandler().logException;
@override
void visitAdjacentStrings(AdjacentStrings node) {
diff --git a/pkg/analyzer/test/generated/utilities_test.dart b/pkg/analyzer/test/generated/utilities_test.dart
index 589c79a..b6d2157 100644
--- a/pkg/analyzer/test/generated/utilities_test.dart
+++ b/pkg/analyzer/test/generated/utilities_test.dart
@@ -5,13 +5,11 @@
import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/testing/ast_test_factory.dart';
-import 'package:analyzer/src/generated/testing/token_factory.dart';
import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -19,7 +17,6 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(BooleanArrayTest);
- defineReflectiveTests(ExceptionHandlingDelegatingAstVisitorTest);
defineReflectiveTests(LineInfoTest);
defineReflectiveTests(NodeReplacerTest);
defineReflectiveTests(SourceRangeTest);
@@ -116,23 +113,6 @@
}
}
-@reflectiveTest
-class ExceptionHandlingDelegatingAstVisitorTest {
- void test_handlerIsCalled() {
- AstVisitor exceptionThrowingVisitor = _ExceptionThrowingVisitor();
- bool handlerInvoked = false;
- AstVisitor visitor = ExceptionHandlingDelegatingAstVisitor(
- [exceptionThrowingVisitor], (AstNode node, AstVisitor visitor,
- dynamic exception, StackTrace stackTrace) {
- handlerInvoked = true;
- });
- astFactory
- .nullLiteral(TokenFactory.tokenFromKeyword(Keyword.NULL))
- .accept(visitor);
- expect(handlerInvoked, isTrue);
- }
-}
-
class Getter_NodeReplacerTest_test_annotation
implements NodeReplacerTest_Getter<Annotation, ArgumentList> {
@override
@@ -2740,10 +2720,3 @@
expect(StringUtilities.substringBeforeChar("abc", 0x64), "abc");
}
}
-
-class _ExceptionThrowingVisitor extends SimpleAstVisitor {
- @override
- visitNullLiteral(NullLiteral node) {
- throw ArgumentError('');
- }
-}
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index ae7a6ad..37bb767 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -1636,10 +1636,9 @@
if (length == 0) {
data->value.as_typed_data.values = NULL;
} else {
- const intptr_t length_in_bytes = length * element_size;
- uint8_t* cdata = d->zone()->Alloc<uint8_t>(length_in_bytes);
- data->value.as_typed_data.values = cdata;
- d->ReadBytes(cdata, length_in_bytes);
+ data->value.as_typed_data.values =
+ const_cast<uint8_t*>(d->CurrentBufferAddress());
+ d->Advance(length * element_size);
}
d->AssignRef(data);
}
diff --git a/tools/VERSION b/tools/VERSION
index 79fac53..60bf783 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 14
PATCH 0
-PRERELEASE 368
+PRERELEASE 369
PRERELEASE_PATCH 0
\ No newline at end of file