blob: 4cd0fba27dce76322ceb57e1a8fbf1de19f38e45 [file] [log] [blame]
// Copyright (c) 2023, 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.
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/dart/analysis/driver_event.dart' as events;
import 'package:analyzer/src/dart/analysis/results.dart';
import 'package:analyzer/src/dart/analysis/status.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/utilities/extensions/file_system.dart';
import 'package:analyzer_utilities/testing/tree_string_sink.dart';
import 'package:test/test.dart';
import '../../../util/element_printer.dart';
import '../../summary/resolved_ast_printer.dart';
sealed class DriverEvent {}
class DriverEventsPrinter {
final DriverEventsPrinterConfiguration configuration;
final TreeStringSink sink;
final ElementPrinter elementPrinter;
final IdProvider idProvider;
DriverEventsPrinter({
required this.configuration,
required this.sink,
required this.elementPrinter,
required this.idProvider,
});
void write(List<DriverEvent> events) {
for (var event in events) {
_writeEvent(event);
}
}
void _writeAnalysisError(AnalysisError e) {
sink.writelnWithIndent('${e.offset} +${e.length} ${e.errorCode.name}');
}
void _writeErrorsEvent(GetErrorsEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
_writeErrorsResult(event.result);
});
}
void _writeErrorsResult(SomeErrorsResult result) {
switch (result) {
case ErrorsResultImpl():
var id = idProvider[result];
sink.writelnWithIndent('ErrorsResult $id');
sink.withIndent(() {
sink.writelnWithIndent('path: ${result.file.posixPath}');
expect(result.path, result.file.path);
sink.writelnWithIndent('uri: ${result.uri}');
sink.writeFlags({
'isAugmentation': result.isAugmentation,
'isLibrary': result.isLibrary,
'isMacroAugmentation': result.isMacroAugmentation,
'isPart': result.isPart,
});
if (configuration.errorsConfiguration.withContentPredicate(result)) {
sink.writelnWithIndent('content');
sink.writeln('---');
sink.write(result.content);
sink.writeln('---');
}
sink.writeElements('errors', result.errors, _writeAnalysisError);
});
default:
throw UnimplementedError('${result.runtimeType}');
}
}
void _writeEvent(DriverEvent event) {
switch (event) {
case GetCachedResolvedUnitEvent():
_writeGetCachedResolvedUnit(event);
case GetErrorsEvent():
_writeErrorsEvent(event);
case GetIndexEvent():
_writeIndexEvent(event);
case GetLibraryByUriEvent():
_writeGetLibraryByUriEvent(event);
case GetResolvedLibraryEvent():
_writeGetResolvedLibrary(event);
case GetResolvedLibraryByUriEvent():
_writeGetResolvedLibraryByUri(event);
case GetResolvedUnitEvent():
_writeGetResolvedUnit(event);
case GetUnitElementEvent():
_writeGetUnitElementEvent(event);
case ResultStreamEvent():
_writeResultStreamEvent(event);
case SchedulerStatusEvent():
_writeSchedulerStatusEvent(event);
}
}
void _writeGetCachedResolvedUnit(GetCachedResolvedUnitEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
if (event.result case var result?) {
_writeResolvedUnitResult(result);
} else {
sink.writelnWithIndent('null');
}
});
}
void _writeGetEvent(GetDriverEvent event) {
sink.writelnWithIndent('[future] ${event.methodName} ${event.name}');
}
void _writeGetLibraryByUriEvent(GetLibraryByUriEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
_writeLibraryElementResult(event.result);
});
}
void _writeGetResolvedLibrary(GetResolvedLibraryEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
_writeResolvedLibraryResult(event.result);
});
}
void _writeGetResolvedLibraryByUri(GetResolvedLibraryByUriEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
_writeResolvedLibraryResult(event.result);
});
}
void _writeGetResolvedUnit(GetResolvedUnitEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
_writeResolvedUnitResult(event.result);
});
}
void _writeGetUnitElementEvent(GetUnitElementEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
var result = event.result;
switch (result) {
case UnitElementResult():
_writeUnitElementResult(result);
default:
throw UnimplementedError('${result.runtimeType}');
}
});
}
void _writeIndexEvent(GetIndexEvent event) {
_writeGetEvent(event);
sink.withIndent(() {
if (event.result case var result?) {
sink.writeElements('strings', result.strings, (str) {
sink.writelnWithIndent(str);
});
}
});
}
void _writeLibraryElementResult(SomeLibraryElementResult result) {
switch (result) {
case CannotResolveUriResult():
sink.writelnWithIndent('CannotResolveUriResult');
case NotLibraryButPartResult():
sink.writelnWithIndent('NotLibraryButPartResult');
default:
throw UnimplementedError('${result.runtimeType}');
}
}
void _writeResolvedLibraryResult(SomeResolvedLibraryResult result) {
switch (result) {
case CannotResolveUriResult():
sink.writelnWithIndent('CannotResolveUriResult');
case NotLibraryButPartResult():
sink.writelnWithIndent('NotLibraryButPartResult');
case ResolvedLibraryResult():
ResolvedLibraryResultPrinter(
configuration: configuration.libraryConfiguration,
sink: sink,
idProvider: idProvider,
elementPrinter: ElementPrinter(
sink: sink,
configuration: ElementPrinterConfiguration(),
),
).write(result);
default:
throw UnimplementedError('${result.runtimeType}');
}
}
void _writeResolvedUnitResult(SomeResolvedUnitResult result) {
ResolvedUnitResultPrinter(
configuration: configuration.libraryConfiguration.unitConfiguration,
sink: sink,
elementPrinter: elementPrinter,
idProvider: idProvider,
libraryElement: null,
).write(result);
}
void _writeResultStreamEvent(ResultStreamEvent event) {
var object = event.object;
switch (object) {
case events.AnalyzeFile():
sink.writelnWithIndent('[operation] analyzeFile');
sink.withIndent(() {
var file = object.file.resource;
sink.writelnWithIndent('file: ${file.posixPath}');
var libraryFile = object.library.file.resource;
sink.writelnWithIndent('library: ${libraryFile.posixPath}');
});
case ErrorsResult():
sink.writelnWithIndent('[stream]');
sink.withIndent(() {
_writeErrorsResult(object);
});
case events.GetErrorsFromBytes():
sink.writelnWithIndent('[operation] getErrorsFromBytes');
sink.withIndent(() {
var file = object.file.resource;
sink.writelnWithIndent('file: ${file.posixPath}');
var libraryFile = object.library.file.resource;
sink.writelnWithIndent('library: ${libraryFile.posixPath}');
});
case ResolvedUnitResult():
if (!configuration.withStreamResolvedUnitResults) {
return;
}
sink.writelnWithIndent('[stream]');
sink.withIndent(() {
_writeResolvedUnitResult(object);
});
default:
throw UnimplementedError('${object.runtimeType}');
}
}
void _writeSchedulerStatusEvent(SchedulerStatusEvent event) {
sink.writeIndentedLine(() {
sink.write('[status] ');
switch (event.status) {
case AnalysisStatusIdle():
sink.write('idle');
case AnalysisStatusWorking():
sink.write('working');
}
});
}
void _writeUnitElementResult(UnitElementResult result) {
sink.writelnWithIndent('path: ${result.file.posixPath}');
expect(result.path, result.file.path);
sink.writelnWithIndent('uri: ${result.uri}');
sink.writeFlags({
'isAugmentation': result.isAugmentation,
'isLibrary': result.isLibrary,
'isMacroAugmentation': result.isMacroAugmentation,
'isPart': result.isPart,
});
var unitElement = result.element;
elementPrinter.writeNamedElement(
'enclosing',
unitElement.enclosingElement3,
);
var elementsToWrite =
configuration.unitElementConfiguration.elementSelector(unitElement);
elementPrinter.writeElementList('selectedElements', elementsToWrite);
}
}
class DriverEventsPrinterConfiguration {
var libraryConfiguration = ResolvedLibraryResultPrinterConfiguration();
var unitElementConfiguration = UnitElementPrinterConfiguration();
var errorsConfiguration = ErrorsResultPrinterConfiguration();
var withStreamResolvedUnitResults = true;
}
class ErrorsResultPrinterConfiguration {
bool Function(FileResult) withContentPredicate = (_) => false;
}
/// The result of `getCachedResolvedUnit`.
final class GetCachedResolvedUnitEvent extends GetDriverEvent {
final SomeResolvedUnitResult? result;
GetCachedResolvedUnitEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getCachedResolvedUnit';
}
sealed class GetDriverEvent extends DriverEvent {
final String name;
GetDriverEvent({
required this.name,
});
String get methodName;
}
/// The result of `getErrors`.
final class GetErrorsEvent extends GetDriverEvent {
final SomeErrorsResult result;
GetErrorsEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getErrors';
}
/// The result of `getIndex`.
final class GetIndexEvent extends GetDriverEvent {
final AnalysisDriverUnitIndex? result;
GetIndexEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getIndex';
}
/// The result of `getLibraryByUri`.
final class GetLibraryByUriEvent extends GetDriverEvent {
final SomeLibraryElementResult result;
GetLibraryByUriEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getLibraryByUri';
}
/// The result of `getResolvedLibraryByUri`.
final class GetResolvedLibraryByUriEvent extends GetDriverEvent {
final SomeResolvedLibraryResult result;
GetResolvedLibraryByUriEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getResolvedLibraryByUri';
}
/// The result of `getResolvedLibrary`.
final class GetResolvedLibraryEvent extends GetDriverEvent {
final SomeResolvedLibraryResult result;
GetResolvedLibraryEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getResolvedLibrary';
}
/// The result of `getResolvedUnit`.
final class GetResolvedUnitEvent extends GetDriverEvent {
final SomeResolvedUnitResult result;
GetResolvedUnitEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getResolvedUnit';
}
/// The result of `getUnitElement`.
final class GetUnitElementEvent extends GetDriverEvent {
final SomeUnitElementResult result;
GetUnitElementEvent({
required super.name,
required this.result,
});
@override
String get methodName => 'getUnitElement';
}
class IdProvider {
final Map<Object, String> _map = Map.identity();
String operator [](Object object) {
return _map[object] ??= '#${_map.length}';
}
String? existing(Object object) {
return _map[object];
}
}
class ResolvedLibraryResultPrinter {
final ResolvedLibraryResultPrinterConfiguration configuration;
final TreeStringSink sink;
final ElementPrinter elementPrinter;
final IdProvider idProvider;
late final LibraryElement _libraryElement;
ResolvedLibraryResultPrinter({
required this.configuration,
required this.sink,
required this.elementPrinter,
required this.idProvider,
});
void write(SomeResolvedLibraryResult result) {
switch (result) {
case ResolvedLibraryResult():
_writeResolvedLibraryResult(result);
default:
throw UnimplementedError('${result.runtimeType}');
}
}
void _writeResolvedLibraryResult(ResolvedLibraryResult result) {
if (idProvider.existing(result) case var id?) {
sink.writelnWithIndent('ResolvedLibraryResult $id');
return;
}
_libraryElement = result.element;
var id = idProvider[result];
sink.writelnWithIndent('ResolvedLibraryResult $id');
sink.withIndent(() {
elementPrinter.writeNamedElement('element', result.element);
sink.writeElements('units', result.units, _writeResolvedUnitResult);
});
}
void _writeResolvedUnitResult(ResolvedUnitResult result) {
ResolvedUnitResultPrinter(
configuration: configuration.unitConfiguration,
sink: sink,
elementPrinter: elementPrinter,
libraryElement: _libraryElement,
idProvider: idProvider,
).write(result);
}
}
class ResolvedLibraryResultPrinterConfiguration {
var unitConfiguration = ResolvedUnitResultPrinterConfiguration();
}
class ResolvedUnitResultPrinter {
final ResolvedUnitResultPrinterConfiguration configuration;
final TreeStringSink sink;
final ElementPrinter elementPrinter;
final LibraryElement? libraryElement;
final IdProvider idProvider;
ResolvedUnitResultPrinter({
required this.configuration,
required this.sink,
required this.elementPrinter,
required this.libraryElement,
required this.idProvider,
});
void write(SomeResolvedUnitResult result) {
switch (result) {
case ResolvedUnitResultImpl():
_writeResolvedUnitResult(result);
default:
throw UnimplementedError('${result.runtimeType}');
}
}
void _writeAnalysisError(AnalysisError e) {
sink.writelnWithIndent('${e.offset} +${e.length} ${e.errorCode.name}');
}
void _writeResolvedUnitResult(ResolvedUnitResultImpl result) {
if (idProvider.existing(result) case var id?) {
sink.writelnWithIndent('ResolvedUnitResult $id');
return;
}
var id = idProvider[result];
sink.writelnWithIndent('ResolvedUnitResult $id');
sink.withIndent(() {
sink.writelnWithIndent('path: ${result.file.posixPath}');
expect(result.path, result.file.path);
sink.writelnWithIndent('uri: ${result.uri}');
// Don't write, just check.
if (libraryElement != null) {
expect(result.libraryElement, same(libraryElement));
}
sink.writeFlags({
'exists': result.exists,
'isAugmentation': result.isAugmentation,
'isLibrary': result.isLibrary,
'isMacroAugmentation': result.isMacroAugmentation,
'isPart': result.isPart,
});
if (configuration.withContentPredicate(result)) {
sink.writelnWithIndent('content');
sink.writeln('---');
sink.write(result.content);
sink.writeln('---');
}
sink.writeElements('errors', result.errors, _writeAnalysisError);
var nodeToWrite = configuration.nodeSelector(result);
if (nodeToWrite != null) {
sink.writeWithIndent('selectedNode: ');
nodeToWrite.accept(
ResolvedAstPrinter(
sink: sink,
elementPrinter: elementPrinter,
configuration: configuration.nodeConfiguration,
),
);
}
var typesToWrite = configuration.typesSelector(result);
sink.writeElements(
'selectedTypes',
typesToWrite.entries.toList(),
(entry) {
sink.writeIndent();
sink.write('${entry.key}: ');
elementPrinter.writeType(entry.value);
},
);
var variableTypesToWrite = configuration.variableTypesSelector(result);
sink.writeElements(
'selectedVariableTypes',
variableTypesToWrite,
(variable) {
sink.writeIndent();
sink.write('${variable.name}: ');
elementPrinter.writeType(variable.type);
},
);
});
}
}
class ResolvedUnitResultPrinterConfiguration {
var nodeConfiguration = ResolvedNodeTextConfiguration();
AstNode? Function(ResolvedUnitResult) nodeSelector = (_) => null;
Map<String, DartType> Function(ResolvedUnitResult) typesSelector = (_) => {};
List<VariableElement> Function(ResolvedUnitResult) variableTypesSelector =
(_) => [];
bool Function(FileResult) withContentPredicate = (_) => false;
}
/// The event of received an object into the `results` stream.
final class ResultStreamEvent extends DriverEvent {
final Object object;
ResultStreamEvent({
required this.object,
});
}
final class SchedulerStatusEvent extends DriverEvent {
final AnalysisStatus status;
SchedulerStatusEvent(this.status);
}
class UnitElementPrinterConfiguration {
List<Element> Function(CompilationUnitElement) elementSelector = (_) => [];
}