dartdev: Tidy and modernize analysis server code

This code seemed a little out of date and non-idiomatic. I used a few modern language features to help it to better comply with our team styles.

* Make declarations private if they can be.
* Make declarations final if they can be.
* Use factory constructors over static methods.
* Do not use type annotations that would be inferred.
* Use patterns for matching JSON data.
* Use extension type when appropriate.

Change-Id: Ib7ecbe51b6d8a94e56a51f84772952362c64049f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508424
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Sam Rawlins
2026-06-02 09:35:48 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 8b4420c381
commit c15aeb3da7
2 changed files with 53 additions and 70 deletions
+52 -69
View File
@@ -32,9 +32,9 @@ preAnalysisServerStart;
/// A class to provide an API wrapper around an analysis server process. /// A class to provide an API wrapper around an analysis server process.
class AnalysisServer { class AnalysisServer {
AnalysisServer( AnalysisServer(
this.packagesFile, this._packagesFile,
this.sdkPath, this._sdkPath,
this.analysisRoots, { this._analysisRoots, {
this.cacheDirectoryPath, this.cacheDirectoryPath,
required this.commandName, required this.commandName,
required this.argResults, required this.argResults,
@@ -46,9 +46,9 @@ class AnalysisServer {
}); });
final String? cacheDirectoryPath; final String? cacheDirectoryPath;
final File? packagesFile; final File? _packagesFile;
final Directory sdkPath; final Directory _sdkPath;
final List<FileSystemEntity> analysisRoots; final List<FileSystemEntity> _analysisRoots;
final String commandName; final String commandName;
final ArgResults? argResults; final ArgResults? argResults;
final List<String> enabledExperiments; final List<String> enabledExperiments;
@@ -113,7 +113,7 @@ class AnalysisServer {
/// Starts the process and returns the pid for it. /// Starts the process and returns the pid for it.
Future<int> start({bool setAnalysisRoots = true}) async { Future<int> start({bool setAnalysisRoots = true}) async {
preAnalysisServerStart?.call(commandName, analysisRoots, argResults); preAnalysisServerStart?.call(commandName, _analysisRoots, argResults);
final process = await _startProcess(); final process = await _startProcess();
_process = process; _process = process;
@@ -160,7 +160,7 @@ class AnalysisServer {
_sendCommand( _sendCommand(
'server.setSubscriptions', 'server.setSubscriptions',
params: <String, dynamic>{ params: {
'subscriptions': <String>['STATUS'], 'subscriptions': <String>['STATUS'],
}, },
); );
@@ -172,7 +172,7 @@ class AnalysisServer {
// The call to `absolute.resolveSymbolicLinksSync()` canonicalizes the path // The call to `absolute.resolveSymbolicLinksSync()` canonicalizes the path
// to be passed to the analysis server. // to be passed to the analysis server.
final analysisRootPaths = [ final analysisRootPaths = [
for (final root in analysisRoots) for (final root in _analysisRoots)
trimEnd( trimEnd(
root.absolute.resolveSymbolicLinksSync(), root.absolute.resolveSymbolicLinksSync(),
path.context.separator, path.context.separator,
@@ -217,9 +217,9 @@ class AnalysisServer {
'--disable-status-notification-debouncing', '--disable-status-notification-debouncing',
'--disable-silent-analysis-exceptions', '--disable-silent-analysis-exceptions',
'--sdk', '--sdk',
sdkPath.path, _sdkPath.path,
if (cacheDirectoryPath != null) '--cache=$cacheDirectoryPath', if (cacheDirectoryPath != null) '--cache=$cacheDirectoryPath',
if (packagesFile != null) '--packages=${packagesFile!.path}', if (_packagesFile != null) '--packages=${_packagesFile.path}',
if (enabledExperiments.isNotEmpty) if (enabledExperiments.isNotEmpty)
'--$experimentFlagName=${enabledExperiments.join(',')}', '--$experimentFlagName=${enabledExperiments.join(',')}',
if (!_usePlugins) '--no-plugins', if (!_usePlugins) '--no-plugins',
@@ -243,7 +243,7 @@ class AnalysisServer {
}) { }) {
return _sendCommand( return _sendCommand(
'edit.bulkFixes', 'edit.bulkFixes',
params: <String, dynamic>{ params: {
'included': [path.canonicalize(filePath)], 'included': [path.canonicalize(filePath)],
'inTestMode': inTestMode, 'inTestMode': inTestMode,
'updatePubspec': updatePubspec, 'updatePubspec': updatePubspec,
@@ -260,23 +260,18 @@ class AnalysisServer {
Future<void> shutdown({Duration? timeout}) async { Future<void> shutdown({Duration? timeout}) async {
// Request shutdown. // Request shutdown.
final Future<void> future = _sendCommand('server.shutdown').then(( var future = _sendCommand('server.shutdown').then((_) {
Map<String, dynamic> value,
) {
_shutdownResponseReceived = true; _shutdownResponseReceived = true;
return;
}); });
await (timeout != null if (timeout != null) {
? future.timeout( future = future.timeout(
timeout, timeout,
onTimeout: () { onTimeout: () {
log.stderr( log.stderr('The analysis server timed out while shutting down.');
'The analysis server timed out while shutting down.', },
); );
}, }
) await future.whenComplete(dispose);
: future)
.whenComplete(dispose);
} }
/// Send an `analysis.updateContent` request with the given [files]. /// Send an `analysis.updateContent` request with the given [files].
@@ -292,7 +287,7 @@ class AnalysisServer {
Map<String, dynamic>? params, Map<String, dynamic>? params,
}) { }) {
final String id = (++_id).toString(); final String id = (++_id).toString();
final String message = json.encode(<String, dynamic>{ final String message = json.encode({
'id': id, 'id': id,
'method': method, 'method': method,
'params': params, 'params': params,
@@ -308,13 +303,12 @@ class AnalysisServer {
return completer.future; return completer.future;
} }
void _handlePluginError(Map<String, dynamic>? error) { void _handlePluginError(Map<String, dynamic> error) {
_serverErrorReceived = true; _serverErrorReceived = true;
final err = error!; // No need for a preamble (like in `_handleServerError`); the message should
// No need for a preamble (like in _handleServerError); the message should
// have all of the context necessary. // have all of the context necessary.
log.stderr(err['message']); log.stderr(error['message']);
final stackTrace = err['stackTrace']; final stackTrace = error['stackTrace'];
if (stackTrace is String && stackTrace.isNotEmpty) { if (stackTrace is String && stackTrace.isNotEmpty) {
log.stderr(stackTrace); log.stderr(stackTrace);
} }
@@ -338,7 +332,7 @@ class AnalysisServer {
_requestCompleters _requestCompleters
.remove(id) .remove(id)
?.completeError( ?.completeError(
RequestError.parse(error.cast<String, dynamic>()), _RequestError.parse(error.cast<String, dynamic>()),
); );
} else { } else {
_requestCompleters.remove(id)?.complete(response['result'] ?? {}); _requestCompleters.remove(id)?.complete(response['result'] ?? {});
@@ -347,9 +341,8 @@ class AnalysisServer {
} }
} }
void _handleServerError(Map<String, dynamic>? error) { void _handleServerError(Map<String, dynamic> error) {
_serverErrorReceived = true; _serverErrorReceived = true;
final err = error!;
log.stderr('An unexpected error was encountered by the Analysis Server.'); log.stderr('An unexpected error was encountered by the Analysis Server.');
log.stderr( log.stderr(
'Please file an issue at ' 'Please file an issue at '
@@ -357,8 +350,8 @@ class AnalysisServer {
'details:\n', 'details:\n',
); );
// Fields are 'isFatal', 'message', and 'stackTrace'. // Fields are 'isFatal', 'message', and 'stackTrace'.
log.stderr(err['message']); log.stderr(error['message']);
final stackTrace = err['stackTrace']; final stackTrace = error['stackTrace'];
if (stackTrace is String && stackTrace.isNotEmpty) { if (stackTrace is String && stackTrace.isNotEmpty) {
log.stderr(stackTrace); log.stderr(stackTrace);
} }
@@ -381,22 +374,21 @@ enum _AnalysisSeverity { error, warning, info, none }
class AnalysisError implements Comparable<AnalysisError> { class AnalysisError implements Comparable<AnalysisError> {
AnalysisError(this.json); AnalysisError(this.json);
static final Map<String, _AnalysisSeverity> _severityMap = static final Map<String, _AnalysisSeverity> _severityMap = {
<String, _AnalysisSeverity>{ 'INFO': _AnalysisSeverity.info,
'INFO': _AnalysisSeverity.info, 'WARNING': _AnalysisSeverity.warning,
'WARNING': _AnalysisSeverity.warning, 'ERROR': _AnalysisSeverity.error,
'ERROR': _AnalysisSeverity.error, };
};
// "severity":"INFO","type":"TODO","location":{ // "severity":"INFO","type":"TODO","location":{
// "file":"/Users/.../lib/test.dart","offset":362,"length":72,"startLine":15,"startColumn":4 // "file":"/Users/.../lib/test.dart","offset":362,"length":72,"startLine":15,"startColumn":4
// },"message":"...","hasFix":false} // },"message":"...","hasFix":false}
Map<String, dynamic> json; final Map<String, dynamic> json;
String? get severity => json['severity'] as String?; String get severity => json['severity'] as String;
_AnalysisSeverity get _severityLevel => _AnalysisSeverity get _severityLevel =>
_severityMap[severity!] ?? _AnalysisSeverity.none; _severityMap[severity] ?? _AnalysisSeverity.none;
bool get isInfo => _severityLevel == _AnalysisSeverity.info; bool get isInfo => _severityLevel == _AnalysisSeverity.info;
@@ -429,18 +421,17 @@ class AnalysisError implements Comparable<AnalysisError> {
String? get url => json['url'] as String?; String? get url => json['url'] as String?;
List<DiagnosticMessage> get contextMessages { List<DiagnosticMessage> get contextMessages {
var messages = json['contextMessages'] as List<dynamic>?; if (json['contextMessages'] case List<dynamic> messages) {
if (messages == null) { return messages.map((message) => DiagnosticMessage(message)).toList();
// The field is optional, so we return an empty list as a default value. } else {
return []; return const [];
} }
return messages.map((message) => DiagnosticMessage(message)).toList();
} }
@override @override
int compareTo(AnalysisError other) { int compareTo(AnalysisError other) {
// Sort in order of severity, file path, error location, and message. // Sort in order of severity, file path, error location, and message.
final int diff = _severityLevel.index - other._severityLevel.index; final diff = _severityLevel.index - other._severityLevel.index;
if (diff != 0) { if (diff != 0) {
return diff; return diff;
} }
@@ -458,16 +449,12 @@ class AnalysisError implements Comparable<AnalysisError> {
@override @override
String toString() => String toString() =>
'${severity!.toLowerCase()}' '${severity.toLowerCase()}'
'$message$file:$startLine:$startColumn' '$message$file:$startLine:$startColumn'
'($code)'; '($code)';
} }
class DiagnosticMessage { extension type DiagnosticMessage(Map<String, dynamic> json) {
final Map<String, dynamic> json;
DiagnosticMessage(this.json);
int? get column => json['location']['startColumn'] as int?; int? get column => json['location']['startColumn'] as int?;
int? get endColumn => json['location']['endColumn'] as int?; int? get endColumn => json['location']['endColumn'] as int?;
@@ -492,20 +479,16 @@ class FileAnalysisErrors {
FileAnalysisErrors(this.file, this.errors); FileAnalysisErrors(this.file, this.errors);
} }
class RequestError { class _RequestError {
static RequestError parse(dynamic error) { factory _RequestError.parse(Map<String, dynamic> error) => _RequestError(
return RequestError( error['code'],
error['code'], error['message'],
error['message'], );
stackTrace: error['stackTrace'],
);
}
final String code; final String code;
final String message; final String message;
final String stackTrace;
RequestError(this.code, this.message, {required this.stackTrace}); _RequestError(this.code, this.message);
@override @override
String toString() => '[RequestError code: $code, message: $message]'; String toString() => '[RequestError code: $code, message: $message]';
+1 -1
View File
@@ -385,7 +385,7 @@ class AnalyzeCommand extends DartdevCommand {
: (dartdevUsageLineLength! - _bodyIndentWidth); : (dartdevUsageLineLength! - _bodyIndentWidth);
for (final AnalysisError error in errors) { for (final AnalysisError error in errors) {
var severity = error.severity!.toLowerCase().padLeft(_severityWidth); var severity = error.severity.toLowerCase().padLeft(_severityWidth);
if (error.isError) { if (error.isError) {
severity = ansi.error(severity); severity = ansi.error(severity);
} }