Migrate lib/src/protocol_server.dart and related.

Change-Id: Ibb1e5a53aa8ad67fa43b315880c491093b051e20
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194201
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov
2021-04-07 00:26:59 +00:00
committed by commit-bot@chromium.org
parent cbada0ac1c
commit d8da40ec52
10 changed files with 401 additions and 381 deletions
@@ -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
/// Utilities for converting Dart entities into analysis server's protocol
/// entities.
import 'package:analysis_server/src/protocol_server.dart';
@@ -130,7 +128,7 @@ String getElementDisplayName(engine.Element element) {
}
}
String _getParametersString(engine.Element element) {
String? _getParametersString(engine.Element element) {
// TODO(scheglov) expose the corresponding feature from ExecutableElement
List<engine.ParameterElement> parameters;
if (element is engine.ExecutableElement) {
@@ -179,8 +177,8 @@ String _getParametersString(engine.Element element) {
return '(' + sb.toString() + ')';
}
String _getTypeParametersString(engine.Element element) {
List<engine.TypeParameterElement> typeParameters;
String? _getTypeParametersString(engine.Element element) {
List<engine.TypeParameterElement>? typeParameters;
if (element is engine.ClassElement) {
typeParameters = element.typeParameters;
} else if (element is engine.TypeAliasElement) {
@@ -2,13 +2,12 @@
// 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/plugin/protocol/protocol_dart.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/search/search_engine.dart'
as engine;
import 'package:analysis_server/src/utilities/extensions/element.dart';
import 'package:analyzer/dart/analysis/results.dart' as engine;
import 'package:analyzer/dart/ast/ast.dart' as engine;
import 'package:analyzer/dart/element/element.dart' as engine;
@@ -35,7 +34,7 @@ List<AnalysisError> doAnalysisError_listFromEngine(
/// Adds [edit] to the file containing the given [element].
void doSourceChange_addElementEdit(
SourceChange change, engine.Element element, SourceEdit edit) {
var source = element.source;
var source = element.source!;
doSourceChange_addSourceEdit(change, source, edit);
}
@@ -47,7 +46,7 @@ void doSourceChange_addSourceEdit(
change.addEdit(file, isNewFile ? -1 : 0, edit);
}
String getAliasedTypeString(engine.Element element) {
String? getAliasedTypeString(engine.Element element) {
if (element is engine.TypeAliasElement) {
var aliasedType = element.aliasedType;
return aliasedType.getDisplayString(withNullability: false);
@@ -55,18 +54,16 @@ String getAliasedTypeString(engine.Element element) {
return null;
}
String getReturnTypeString(engine.Element element) {
String? getReturnTypeString(engine.Element element) {
if (element is engine.ExecutableElement) {
if (element.kind == engine.ElementKind.SETTER) {
return null;
} else {
return element.returnType?.getDisplayString(withNullability: false);
return element.returnType.getDisplayString(withNullability: false);
}
} else if (element is engine.VariableElement) {
var type = element.type;
return type != null
? type.getDisplayString(withNullability: false)
: 'dynamic';
return type.getDisplayString(withNullability: false);
} else if (element is engine.TypeAliasElement) {
var aliasedType = element.aliasedType;
if (aliasedType is FunctionType) {
@@ -107,7 +104,7 @@ List<T> mapEngineErrors<T>(
/// If an [errorSeverity] is specified, it will override the one in [error].
AnalysisError newAnalysisError_fromEngine(
engine.ResolvedUnitResult result, engine.AnalysisError error,
[engine.ErrorSeverity errorSeverity]) {
[engine.ErrorSeverity? errorSeverity]) {
var errorCode = error.errorCode;
// prepare location
Location location;
@@ -115,23 +112,16 @@ AnalysisError newAnalysisError_fromEngine(
var file = error.source.fullName;
var offset = error.offset;
var length = error.length;
var startLine = -1;
var startColumn = -1;
var endLine = -1;
var endColumn = -1;
var lineInfo = result.lineInfo;
if (lineInfo != null) {
var startLocation = lineInfo.getLocation(offset);
if (startLocation != null) {
startLine = startLocation.lineNumber;
startColumn = startLocation.columnNumber;
}
var endLocation = lineInfo.getLocation(offset + length);
if (endLocation != null) {
endLine = endLocation.lineNumber;
endColumn = endLocation.columnNumber;
}
}
var startLocation = lineInfo.getLocation(offset);
var startLine = startLocation.lineNumber;
var startColumn = startLocation.columnNumber;
var endLocation = lineInfo.getLocation(offset + length);
var endLine = endLocation.lineNumber;
var endColumn = endLocation.columnNumber;
location = Location(
file, offset, length, startLine, startColumn, endLine, endColumn);
}
@@ -144,7 +134,7 @@ AnalysisError newAnalysisError_fromEngine(
var type = AnalysisErrorType(errorCode.type.name);
var message = error.message;
var code = errorCode.name.toLowerCase();
List<DiagnosticMessage> contextMessages;
List<DiagnosticMessage>? contextMessages;
if (error.contextMessages.isNotEmpty) {
contextMessages = error.contextMessages
.map((message) => newDiagnosticMessage(result, message))
@@ -182,7 +172,7 @@ DiagnosticMessage newDiagnosticMessage(
}
/// Create a Location based on an [engine.Element].
Location newLocation_fromElement(engine.Element element) {
Location? newLocation_fromElement(engine.Element? element) {
if (element == null || element.source == null) {
return null;
}
@@ -206,8 +196,8 @@ Location newLocation_fromMatch(engine.SearchMatch match) {
/// Create a Location based on an [engine.AstNode].
Location newLocation_fromNode(engine.AstNode node) {
var unit = node.thisOrAncestorOfType<engine.CompilationUnit>();
var unitElement = unit.declaredElement;
var unit = node.thisOrAncestorOfType<engine.CompilationUnit>()!;
var unitElement = unit.declaredElement!;
var range = engine.SourceRange(node.offset, node.length);
return _locationForArgs(unitElement, range);
}
@@ -215,13 +205,13 @@ Location newLocation_fromNode(engine.AstNode node) {
/// Create a Location based on an [engine.CompilationUnit].
Location newLocation_fromUnit(
engine.CompilationUnit unit, engine.SourceRange range) {
return _locationForArgs(unit.declaredElement, range);
return _locationForArgs(unit.declaredElement!, range);
}
/// Construct based on an element from the analyzer engine.
OverriddenMember newOverriddenMember_fromEngine(engine.Element member) {
var element = convertElement(member);
var className = member.enclosingElement.displayName;
var className = member.enclosingElement!.displayName;
return OverriddenMember(element, className);
}
@@ -258,22 +248,19 @@ SearchResultKind newSearchResultKind_fromEngine(engine.MatchKind kind) {
/// Construct based on a SourceRange.
SourceEdit newSourceEdit_range(engine.SourceRange range, String replacement,
{String id}) {
{String? id}) {
return SourceEdit(range.offset, range.length, replacement, id: id);
}
List<Element> _computePath(engine.Element element) {
var path = <Element>[];
while (element != null) {
path.add(convertElement(element));
// go up
if (element is engine.PrefixElement) {
// imports are library children, but they are physically in the unit
engine.LibraryElement library = element.enclosingElement;
element = library.definingCompilationUnit;
} else {
element = element.enclosingElement;
}
if (element is engine.PrefixElement) {
element = element.enclosingElement.definingCompilationUnit;
}
for (var e in element.withAncestors) {
path.add(convertElement(e));
}
return path;
}
@@ -282,18 +269,23 @@ engine.CompilationUnitElement _getUnitElement(engine.Element element) {
if (element is engine.CompilationUnitElement) {
return element;
}
if (element?.enclosingElement is engine.LibraryElement) {
element = element.enclosingElement;
var enclosingElement = element.enclosingElement;
if (enclosingElement is engine.LibraryElement) {
element = enclosingElement;
}
if (element is engine.LibraryElement) {
return element.definingCompilationUnit;
}
for (; element != null; element = element.enclosingElement) {
if (element is engine.CompilationUnitElement) {
return element;
for (var e in element.withAncestors) {
if (e is engine.CompilationUnitElement) {
return e;
}
}
return null;
throw StateError('No unit: $element');
}
/// Creates a new [Location].
@@ -40,6 +40,19 @@ extension ElementExtension on Element {
return ancestor is CompilationUnitElement &&
ancestor.enclosingElement.hasDeprecated;
}
/// Return this element and all its enclosing elements.
Iterable<Element> get withAncestors sync* {
var current = this;
while (true) {
yield current;
var enclosing = current.enclosingElement;
if (enclosing == null) {
break;
}
current = enclosing;
}
}
}
extension MethodElementExtensions on MethodElement {
@@ -32,6 +32,7 @@ import 'package:test/test.dart' hide expect;
import 'package:test/test.dart' as test show expect;
import '../mocks.dart';
import '../mocks_lsp.dart';
import '../src/utilities/mock_packages.dart';
const dartLanguageId = 'dart';
+21 -236
View File
@@ -2,27 +2,17 @@
// 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:convert';
import 'package:analysis_server/lsp_protocol/protocol_generated.dart' as lsp;
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart' as lsp;
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/lsp/channel/lsp_channel.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/timestamped_data.dart';
import 'package:http/http.dart' as http;
import 'package:test/test.dart';
const _jsonEncoder = JsonEncoder.withIndent(' ');
/// A [Matcher] that check that the given [Response] has an expected identifier
/// and has an error. The error code may optionally be checked.
Matcher isResponseFailure(String id, [RequestErrorCode code]) =>
Matcher isResponseFailure(String id, [RequestErrorCode? code]) =>
_IsResponseFailure(id, code);
/// A [Matcher] that check that the given [Response] has an expected identifier
@@ -30,7 +20,7 @@ Matcher isResponseFailure(String id, [RequestErrorCode code]) =>
Matcher isResponseSuccess(String id) => _IsResponseSuccess(id);
class MockHttpClient extends http.BaseClient {
Future<http.Response> Function(http.BaseRequest request) sendHandler;
late Future<http.Response> Function(http.BaseRequest request) sendHandler;
int sendHandlerCalls = 0;
bool wasClosed = false;
@@ -57,242 +47,36 @@ class MockHttpClient extends http.BaseClient {
}
}
/// A mock [LspServerCommunicationChannel] for testing [LspAnalysisServer].
class MockLspServerChannel implements LspServerCommunicationChannel {
final StreamController<lsp.Message> _clientToServer =
StreamController<lsp.Message>.broadcast();
final StreamController<lsp.Message> _serverToClient =
StreamController<lsp.Message>.broadcast();
String name;
/// Completer that will be signalled when the input stream is closed.
final Completer _closed = Completer();
/// Errors popups sent to the user.
final shownErrors = <lsp.ShowMessageParams>[];
/// Warning popups sent to the user.
final shownWarnings = <lsp.ShowMessageParams>[];
MockLspServerChannel(bool _printMessages) {
if (_printMessages) {
_serverToClient.stream
.listen((message) => print('<== ' + jsonEncode(message)));
_clientToServer.stream
.listen((message) => print('==> ' + jsonEncode(message)));
}
// Keep track of any errors/warnings that are sent to the user with
// `window/showMessage`.
_serverToClient.stream.listen((message) {
if (message is lsp.NotificationMessage &&
message.method == Method.window_showMessage &&
message.params is lsp.ShowMessageParams) {
if (message.params?.type == MessageType.Error) {
shownErrors.add(message.params);
} else if (message.params?.type == MessageType.Warning) {
shownWarnings.add(message.params);
}
}
});
}
/// Future that will be completed when the input stream is closed.
class MockSource implements Source {
@override
Future get closed {
return _closed.future;
}
final String fullName;
Stream<lsp.Message> get serverToClient => _serverToClient.stream;
MockSource({
this.fullName = 'mocked.dart',
});
@override
void close() {
if (!_closed.isCompleted) {
_closed.complete();
}
if (!_serverToClient.isClosed) {
_serverToClient.close();
}
if (!_clientToServer.isClosed) {
_clientToServer.close();
}
}
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
void listen(void Function(lsp.Message message) onMessage,
{Function onError, void Function() onDone}) {
_clientToServer.stream.listen(onMessage, onError: onError, onDone: onDone);
}
@override
void sendNotification(lsp.NotificationMessage notification) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
notification = _convertJson(notification, lsp.NotificationMessage.fromJson);
_serverToClient.add(notification);
}
void sendNotificationToServer(lsp.NotificationMessage notification) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
notification = _convertJson(notification, lsp.NotificationMessage.fromJson);
// Wrap send request in future to simulate WebSocket.
Future(() => _clientToServer.add(notification));
}
@override
void sendRequest(lsp.RequestMessage request) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
request = _convertJson(request, lsp.RequestMessage.fromJson);
_serverToClient.add(request);
}
/// Send the given [request] to the server and return a future that will
/// complete when a response associated with the [request] has been received.
/// The value of the future will be the received response.
Future<lsp.ResponseMessage> sendRequestToServer(lsp.RequestMessage request) {
// No further requests should be sent after the connection is closed.
if (_closed.isCompleted) {
throw Exception('sendLspRequest after connection closed');
}
request = _convertJson(request, lsp.RequestMessage.fromJson);
// Wrap send request in future to simulate WebSocket.
Future(() => _clientToServer.add(request));
return waitForResponse(request);
}
@override
void sendResponse(lsp.ResponseMessage response) {
// Don't deliver responses after the connection is closed.
if (_closed.isCompleted) {
return;
}
response = _convertJson(response, lsp.ResponseMessage.fromJson);
// Wrap send response in future to simulate WebSocket.
Future(() => _serverToClient.add(response));
}
void sendResponseToServer(lsp.ResponseMessage response) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
response = _convertJson(response, lsp.ResponseMessage.fromJson);
_clientToServer.add(response);
}
/// Return a future that will complete when a response associated with the
/// given [request] has been received. The value of the future will be the
/// received response. The returned future will throw an exception if a server
/// error is reported before the response has been received.
///
/// Unlike [sendLspRequest], this method assumes that the [request] has
/// already been sent to the server.
Future<lsp.ResponseMessage> waitForResponse(
lsp.RequestMessage request, {
bool throwOnError = true,
}) async {
final response = await _serverToClient.stream.firstWhere((message) =>
(message is lsp.ResponseMessage && message.id == request.id) ||
(throwOnError &&
message is lsp.NotificationMessage &&
message.method == Method.window_showMessage &&
lsp.ShowMessageParams.fromJson(message.params).type ==
MessageType.Error));
if (response is lsp.ResponseMessage) {
return response;
} else {
throw 'An error occurred while waiting for a response to ${request.method}: '
'${_jsonEncoder.convert(response.toJson())}';
}
}
/// Round trips the object to JSON and back to ensure it behaves the same as
/// when running over the real STDIO server. Without this, the object passed
/// to the handlers will have concrete types as constructed in tests rather
/// than the maps as they would be (the server expects to do the conversion).
T _convertJson<T>(
lsp.ToJsonable message, T Function(Map<String, dynamic>) constructor) {
return constructor(jsonDecode(jsonEncode(message.toJson())));
}
}
class MockSource extends StringTypedMock implements Source {
@override
TimestampedData<String> contents;
@override
String encoding;
@override
String fullName;
@override
bool isInSystemLibrary;
@override
Source librarySource;
@override
int modificationStamp;
@override
String shortName;
@override
Source source;
@override
Uri uri;
@override
UriKind uriKind;
MockSource([String name = 'mocked.dart']) : super(name);
@override
bool exists() => null;
String toString() => fullName;
}
class StringTypedMock {
final String _toString;
final String? _toString;
StringTypedMock(this._toString);
@override
String toString() {
if (_toString != null) {
return _toString;
}
return super.toString();
return _toString ?? super.toString();
}
}
/// A [Matcher] that check that there are no `error` in a given [Response].
class _IsResponseFailure extends Matcher {
final String _id;
final RequestErrorCode _code;
final RequestErrorCode? _code;
_IsResponseFailure(this._id, this._code);
@@ -300,8 +84,9 @@ class _IsResponseFailure extends Matcher {
Description describe(Description description) {
description =
description.add('response with identifier "$_id" and an error');
if (_code != null) {
description = description.add(' with code ${_code.name}');
var code = _code;
if (code != null) {
description = description.add(' with code ${code.name}');
}
return description;
}
@@ -316,8 +101,7 @@ class _IsResponseFailure extends Matcher {
if (error == null) {
mismatchDescription.add(' and has no error');
} else {
mismatchDescription
.add(' and has error code ${response.error.code.name}');
mismatchDescription.add(' and has error code ${error.code.name}');
}
return mismatchDescription;
}
@@ -325,10 +109,11 @@ class _IsResponseFailure extends Matcher {
@override
bool matches(item, Map matchState) {
Response response = item;
if (response.id != _id || response.error == null) {
var error = response.error;
if (response.id != _id || error == null) {
return false;
}
if (_code != null && response.error.code != _code) {
if (_code != null && error.code != _code) {
return false;
}
return true;
@@ -350,7 +135,7 @@ class _IsResponseSuccess extends Matcher {
@override
Description describeMismatch(
item, Description mismatchDescription, Map matchState, bool verbose) {
Response response = item;
Response? response = item;
if (response == null) {
mismatchDescription.add('is null response');
} else {
@@ -366,7 +151,7 @@ class _IsResponseSuccess extends Matcher {
@override
bool matches(item, Map matchState) {
Response response = item;
Response? response = item;
return response != null && response.id == _id && response.error == null;
}
}
+196
View File
@@ -0,0 +1,196 @@
// Copyright (c) 2014, 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 'dart:async';
import 'dart:convert';
import 'package:analysis_server/lsp_protocol/protocol_generated.dart' as lsp;
import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart' as lsp;
import 'package:analysis_server/src/lsp/channel/lsp_channel.dart';
const _jsonEncoder = JsonEncoder.withIndent(' ');
/// A mock [LspServerCommunicationChannel] for testing [LspAnalysisServer].
class MockLspServerChannel implements LspServerCommunicationChannel {
final StreamController<lsp.Message> _clientToServer =
StreamController<lsp.Message>.broadcast();
final StreamController<lsp.Message> _serverToClient =
StreamController<lsp.Message>.broadcast();
String name;
/// Completer that will be signalled when the input stream is closed.
final Completer _closed = Completer();
/// Errors popups sent to the user.
final shownErrors = <lsp.ShowMessageParams>[];
/// Warning popups sent to the user.
final shownWarnings = <lsp.ShowMessageParams>[];
MockLspServerChannel(bool _printMessages) {
if (_printMessages) {
_serverToClient.stream
.listen((message) => print('<== ' + jsonEncode(message)));
_clientToServer.stream
.listen((message) => print('==> ' + jsonEncode(message)));
}
// Keep track of any errors/warnings that are sent to the user with
// `window/showMessage`.
_serverToClient.stream.listen((message) {
if (message is lsp.NotificationMessage &&
message.method == Method.window_showMessage &&
message.params is lsp.ShowMessageParams) {
if (message.params?.type == MessageType.Error) {
shownErrors.add(message.params);
} else if (message.params?.type == MessageType.Warning) {
shownWarnings.add(message.params);
}
}
});
}
/// Future that will be completed when the input stream is closed.
@override
Future get closed {
return _closed.future;
}
Stream<lsp.Message> get serverToClient => _serverToClient.stream;
@override
void close() {
if (!_closed.isCompleted) {
_closed.complete();
}
if (!_serverToClient.isClosed) {
_serverToClient.close();
}
if (!_clientToServer.isClosed) {
_clientToServer.close();
}
}
@override
void listen(void Function(lsp.Message message) onMessage,
{Function onError, void Function() onDone}) {
_clientToServer.stream.listen(onMessage, onError: onError, onDone: onDone);
}
@override
void sendNotification(lsp.NotificationMessage notification) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
notification = _convertJson(notification, lsp.NotificationMessage.fromJson);
_serverToClient.add(notification);
}
void sendNotificationToServer(lsp.NotificationMessage notification) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
notification = _convertJson(notification, lsp.NotificationMessage.fromJson);
// Wrap send request in future to simulate WebSocket.
Future(() => _clientToServer.add(notification));
}
@override
void sendRequest(lsp.RequestMessage request) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
request = _convertJson(request, lsp.RequestMessage.fromJson);
_serverToClient.add(request);
}
/// Send the given [request] to the server and return a future that will
/// complete when a response associated with the [request] has been received.
/// The value of the future will be the received response.
Future<lsp.ResponseMessage> sendRequestToServer(lsp.RequestMessage request) {
// No further requests should be sent after the connection is closed.
if (_closed.isCompleted) {
throw Exception('sendLspRequest after connection closed');
}
request = _convertJson(request, lsp.RequestMessage.fromJson);
// Wrap send request in future to simulate WebSocket.
Future(() => _clientToServer.add(request));
return waitForResponse(request);
}
@override
void sendResponse(lsp.ResponseMessage response) {
// Don't deliver responses after the connection is closed.
if (_closed.isCompleted) {
return;
}
response = _convertJson(response, lsp.ResponseMessage.fromJson);
// Wrap send response in future to simulate WebSocket.
Future(() => _serverToClient.add(response));
}
void sendResponseToServer(lsp.ResponseMessage response) {
// Don't deliver notifications after the connection is closed.
if (_closed.isCompleted) {
return;
}
response = _convertJson(response, lsp.ResponseMessage.fromJson);
_clientToServer.add(response);
}
/// Return a future that will complete when a response associated with the
/// given [request] has been received. The value of the future will be the
/// received response. The returned future will throw an exception if a server
/// error is reported before the response has been received.
///
/// Unlike [sendLspRequest], this method assumes that the [request] has
/// already been sent to the server.
Future<lsp.ResponseMessage> waitForResponse(
lsp.RequestMessage request, {
bool throwOnError = true,
}) async {
final response = await _serverToClient.stream.firstWhere((message) =>
(message is lsp.ResponseMessage && message.id == request.id) ||
(throwOnError &&
message is lsp.NotificationMessage &&
message.method == Method.window_showMessage &&
lsp.ShowMessageParams.fromJson(message.params).type ==
MessageType.Error));
if (response is lsp.ResponseMessage) {
return response;
} else {
throw 'An error occurred while waiting for a response to ${request.method}: '
'${_jsonEncoder.convert(response.toJson())}';
}
}
/// Round trips the object to JSON and back to ensure it behaves the same as
/// when running over the real STDIO server. Without this, the object passed
/// to the handlers will have concrete types as constructed in tests rather
/// than the maps as they would be (the server expects to do the conversion).
T _convertJson<T>(
lsp.ToJsonable message, T Function(Map<String, dynamic>) constructor) {
return constructor(jsonDecode(jsonEncode(message.toJson())));
}
}
@@ -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:mirrors';
import 'package:analysis_server/src/protocol_server.dart'
@@ -34,37 +32,45 @@ void main() {
@reflectiveTest
class AnalysisErrorTest {
MockSource source = MockSource();
MockAnalysisError engineError;
ResolvedUnitResult result;
late MockSource source;
late MockAnalysisError engineError;
late ResolvedUnitResult result;
void setUp() {
// prepare Source
source.fullName = 'foo.dart';
source = MockSource(fullName: 'foo.dart');
// prepare AnalysisError
engineError = MockAnalysisError(source,
engine.CompileTimeErrorCode.AMBIGUOUS_EXPORT, 10, 20, 'my message');
engineError = MockAnalysisError(
source: source,
errorCode: engine.CompileTimeErrorCode.AMBIGUOUS_EXPORT,
offset: 10,
length: 20,
message: 'my message',
);
// prepare ResolvedUnitResult
var lineInfo = engine.LineInfo([0, 5, 9, 20]);
result = engine.ResolvedUnitResultImpl(null, 'foo.dart', null, true, null,
lineInfo, false, null, [engineError]);
}
void tearDown() {
source = null;
engineError = null;
result = _ResolvedUnitResultImplMock(
lineInfo: lineInfo,
errors: [engineError],
);
}
void test_fromEngine_hasContextMessage() {
engineError.contextMessages.add(engine.DiagnosticMessageImpl(
filePath: 'bar.dart', offset: 30, length: 5, message: 'context'));
var session = MockAnalysisSession();
session.addFileResult(engine.FileResultImpl(
session, 'bar.dart', null, engine.LineInfo([0, 5, 9, 20]), false));
engineError.contextMessages.add(
engine.DiagnosticMessageImpl(
filePath: 'bar.dart',
offset: 30,
length: 5,
message: 'context',
),
);
var error = newAnalysisError_fromEngine(
engine.ResolvedUnitResultImpl(session, 'foo.dart', null, true, null,
engine.LineInfo([0, 5, 9, 20]), false, null, [engineError]),
engineError);
_ResolvedUnitResultImplMock(
lineInfo: engine.LineInfo([0, 5, 9, 20]),
errors: [engineError],
),
engineError,
);
expect(error.toJson(), {
SEVERITY: 'ERROR',
TYPE: 'COMPILE_TIME_ERROR',
@@ -99,7 +105,14 @@ class AnalysisErrorTest {
}
void test_fromEngine_hasCorrection() {
engineError.correction = 'my correction';
engineError = MockAnalysisError(
source: source,
errorCode: engine.CompileTimeErrorCode.AMBIGUOUS_EXPORT,
offset: 10,
length: 20,
message: 'my message',
correction: 'my correction',
);
var error = newAnalysisError_fromEngine(result, engineError);
expect(error.toJson(), {
SEVERITY: 'ERROR',
@@ -123,11 +136,12 @@ class AnalysisErrorTest {
void test_fromEngine_hasUrl() {
engineError = MockAnalysisError(
source,
MockErrorCode(url: 'http://codes.dartlang.org/TEST_ERROR'),
10,
20,
'my message');
source: source,
errorCode: MockErrorCode(url: 'http://codes.dartlang.org/TEST_ERROR'),
offset: 10,
length: 20,
message: 'my message',
);
var error = newAnalysisError_fromEngine(result, engineError);
expect(error.toJson(), {
SEVERITY: 'ERROR',
@@ -150,11 +164,12 @@ class AnalysisErrorTest {
void test_fromEngine_lint() {
engineError = MockAnalysisError(
source,
LintCode('my_lint', 'my message', correction: 'correction'),
10,
20,
'my message');
source: source,
errorCode: LintCode('my_lint', 'my message', correction: 'correction'),
offset: 10,
length: 20,
message: 'my message',
);
var error = newAnalysisError_fromEngine(result, engineError);
expect(error.toJson(), {
SEVERITY: 'INFO',
@@ -176,7 +191,13 @@ class AnalysisErrorTest {
}
void test_fromEngine_noCorrection() {
engineError.correction = null;
engineError = MockAnalysisError(
source: source,
errorCode: engine.CompileTimeErrorCode.AMBIGUOUS_EXPORT,
offset: 10,
length: 20,
message: 'my message',
);
var error = newAnalysisError_fromEngine(result, engineError);
expect(error.toJson(), {
SEVERITY: 'ERROR',
@@ -196,31 +217,6 @@ class AnalysisErrorTest {
HAS_FIX: false
});
}
void test_fromEngine_noLineInfo() {
engineError.correction = null;
var error = newAnalysisError_fromEngine(
engine.ResolvedUnitResultImpl(null, 'foo.dart', null, true, null, null,
false, null, [engineError]),
engineError);
expect(error.toJson(), {
SEVERITY: 'ERROR',
TYPE: 'COMPILE_TIME_ERROR',
LOCATION: {
FILE: 'foo.dart',
OFFSET: 10,
LENGTH: 20,
START_LINE: -1,
START_COLUMN: -1,
END_LINE: -1,
END_COLUMN: -1,
},
MESSAGE: 'my message',
CODE: 'ambiguous_export',
URL: 'https://dart.dev/tools/diagnostic-messages#ambiguous_export',
HAS_FIX: false
});
}
}
@reflectiveTest
@@ -271,7 +267,7 @@ class EnumTester<EngineEnum, ApiEnum> {
/// If the corresponding value is an [ApiEnum], then we check that converting
/// the given key results in the given value.
void run(ApiEnum Function(EngineEnum) convert,
{Map<EngineEnum, ApiEnum> exceptions = const {}}) {
{Map<EngineEnum, ApiEnum?> exceptions = const {}}) {
var engineClass = reflectClass(EngineEnum);
engineClass.staticMembers.forEach((Symbol symbol, MethodMirror method) {
if (symbol == #values) {
@@ -302,49 +298,73 @@ class EnumTester<EngineEnum, ApiEnum> {
}
class MockAnalysisError implements engine.AnalysisError {
@override
MockSource source;
@override
engine.ErrorCode errorCode;
@override
int offset;
@override
String message;
@override
String correction;
@override
int length;
final MockSource? _source;
final engine.ErrorCode? _errorCode;
final int? _offset;
final int? _length;
final String? _message;
final String? _correction;
final DiagnosticMessage? _problemMessage;
final String? _correctionMessage;
@override
List<DiagnosticMessage> contextMessages = <DiagnosticMessage>[];
MockAnalysisError(
this.source, this.errorCode, this.offset, this.length, this.message);
MockAnalysisError({
MockSource? source,
engine.ErrorCode? errorCode,
int? offset,
int? length,
String? message,
String? correction,
DiagnosticMessage? problemMessage,
String? correctionMessage,
}) : _source = source,
_errorCode = errorCode,
_offset = offset,
_length = length,
_message = message,
_correction = correction,
_problemMessage = problemMessage,
_correctionMessage = correctionMessage;
@override
String get correctionMessage => null;
String? get correction => _correction;
@override
DiagnosticMessage get problemMessage => null;
String? get correctionMessage => _correctionMessage;
@override
Severity get severity => null;
engine.ErrorCode get errorCode => _errorCode!;
@override
int get length => _length!;
@override
String get message => _message!;
@override
int get offset => _offset!;
@override
DiagnosticMessage get problemMessage => _problemMessage!;
@override
Severity get severity => throw UnimplementedError();
@override
engine.Source get source => _source!;
}
class MockAnalysisSession implements AnalysisSession {
Map<String, FileResult> fileResults = {};
void addFileResult(FileResult result) {
fileResults[result.path] = result;
fileResults[result.path!] = result;
}
@override
FileResult getFile(String path) => fileResults[path];
FileResult getFile(String path) => fileResults[path]!;
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@@ -361,7 +381,7 @@ class MockErrorCode implements engine.ErrorCode {
String name;
@override
String url;
String? url;
MockErrorCode(
{this.type = engine.ErrorType.COMPILE_TIME_ERROR,
@@ -393,3 +413,19 @@ class MockErrorCode implements engine.ErrorCode {
throw StateError('Unexpected invocation of uniqueName');
}
}
class _ResolvedUnitResultImplMock implements engine.ResolvedUnitResultImpl {
@override
final engine.LineInfo lineInfo;
@override
final List<engine.AnalysisError> errors;
_ResolvedUnitResultImplMock({
required this.lineInfo,
required this.errors,
});
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
@@ -59,7 +59,8 @@ abstract class AbstractTransformOverrideSetParserTest {
void parse(String content) {
errorListener = GatheringErrorListener();
var errorReporter = ErrorReporter(errorListener, MockSource('data.yaml'));
var errorReporter =
ErrorReporter(errorListener, MockSource(fullName: 'data.yaml'));
var parser = TransformOverrideSetParser(errorReporter);
result = parser.parse(content);
}
@@ -42,7 +42,8 @@ abstract class AbstractTransformSetParserTest {
void parse(String content) {
errorListener = GatheringErrorListener();
var errorReporter = ErrorReporter(errorListener, MockSource('data.yaml'));
var errorReporter =
ErrorReporter(errorListener, MockSource(fullName: 'data.yaml'));
var parser = TransformSetParser(errorReporter, 'myPackage');
result = parser.parse(content);
}
@@ -489,9 +489,6 @@ class FileResolver {
}
});
results.forEach((key, value) {
print('$key: $value');
});
var resolvedUnits = results.values.map((fileResult) {
var file = fileResult.file;
return ResolvedUnitResultImpl(