blob: 1f7cb30cca7810267136ef3f11a964f944e51e68 [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/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:test/test.dart';
import 'tree_string_sink.dart';
class ElementPrinter {
final TreeStringSink _sink;
final ElementPrinterConfiguration _configuration;
final String? _selfUriStr;
ElementPrinter({
required TreeStringSink sink,
required ElementPrinterConfiguration configuration,
required String? selfUriStr,
}) : _sink = sink,
_configuration = configuration,
_selfUriStr = selfUriStr;
void writeDirectiveUri(DirectiveUri? uri) {
if (uri == null) {
_sink.writeln('<null>');
} else if (uri is DirectiveUriWithAugmentation) {
_sink.writeln('DirectiveUriWithAugmentation');
_sink.withIndent(() {
final uriStr = _stringOfSource(uri.augmentation.source);
_sink.writelnWithIndent('uri: $uriStr');
});
} else if (uri is DirectiveUriWithLibrary) {
_sink.writeln('DirectiveUriWithLibrary');
_sink.withIndent(() {
final uriStr = _stringOfSource(uri.library.source);
_sink.writelnWithIndent('uri: $uriStr');
});
} else if (uri is DirectiveUriWithUnit) {
_sink.writeln('DirectiveUriWithUnit');
_sink.withIndent(() {
final uriStr = _stringOfSource(uri.unit.source);
_sink.writelnWithIndent('uri: $uriStr');
});
} else if (uri is DirectiveUriWithSource) {
_sink.writeln('DirectiveUriWithSource');
_sink.withIndent(() {
final uriStr = _stringOfSource(uri.source);
_sink.writelnWithIndent('source: $uriStr');
});
} else if (uri is DirectiveUriWithRelativeUri) {
_sink.writeln('DirectiveUriWithRelativeUri');
_sink.withIndent(() {
_sink.writelnWithIndent('relativeUri: ${uri.relativeUri}');
});
} else if (uri is DirectiveUriWithRelativeUriString) {
_sink.writeln('DirectiveUriWithRelativeUriString');
_sink.withIndent(() {
_sink.writelnWithIndent('relativeUriString: ${uri.relativeUriString}');
});
} else {
_sink.writeln('DirectiveUri');
}
}
void writeElement(Element? element) {
switch (element) {
case null:
_sink.writeln('<null>');
case Member():
_writeMember(element);
case MultiplyDefinedElement():
_sink.writeln('<null>');
case AugmentationImportElement():
_writeAugmentationImportElement(element);
case LibraryExportElement():
_writeLibraryExportElement(element);
case LibraryImportElement():
_writeLibraryImportElement(element);
case PartElement():
_writePartElement(element);
default:
final referenceStr = _elementToReferenceString(element);
_sink.writeln(referenceStr);
}
}
void writeElementList(String name, List<Element> elements) {
_sink.writeElements(name, elements, (element) {
_sink.writeIndent();
writeElement(element);
});
}
void writeNamedElement(String name, Element? element) {
_sink.writeWithIndent('$name: ');
writeElement(element);
}
void writeNamedType(String name, DartType? type) {
_sink.writeWithIndent('$name: ');
writeType(type);
}
void writeReference(Reference reference) {
final str = _referenceToString(reference);
_sink.write(str);
}
void writeType(DartType? type) {
if (type != null) {
var typeStr = _typeStr(type);
_sink.writeln(typeStr);
if (type is InterfaceType) {
if (_configuration.withInterfaceTypeElements) {
_sink.withIndent(() {
writeNamedElement('element', type.element);
});
}
}
var alias = type.alias;
if (alias != null) {
_sink.withIndent(() {
writeNamedElement('alias', alias.element);
_sink.withIndent(() {
writeTypeList('typeArguments', alias.typeArguments);
});
});
}
} else {
_sink.writeln('null');
}
}
void writeTypeList(String name, List<DartType>? types) {
if (types != null && types.isNotEmpty) {
_sink.writelnWithIndent(name);
_sink.withIndent(() {
for (final type in types) {
_sink.writeIndent();
writeType(type);
}
});
}
}
String _elementToReferenceString(Element element) {
final enclosingElement = element.enclosingElement;
final reference = (element as ElementImpl).reference;
if (reference != null) {
return _referenceToString(reference);
} else if (element is ParameterElement &&
enclosingElement is! GenericFunctionTypeElement) {
// Positional parameters don't have actual references.
// But we fabricate one to make the output better.
final enclosingStr = enclosingElement != null
? _elementToReferenceString(enclosingElement)
: 'root';
return '$enclosingStr::@parameter::${element.name}';
} else if (element is JoinPatternVariableElementImpl) {
return [
if (!element.isConsistent) 'notConsistent ',
if (element.isFinal) 'final ',
element.name,
'[',
element.variables.map(_elementToReferenceString).join(', '),
']',
].join('');
} else {
return '${element.name}@${element.nameOffset}';
}
}
String _referenceToString(Reference reference) {
var parent = reference.parent!;
if (parent.parent == null) {
var libraryUriStr = reference.name;
if (libraryUriStr == _selfUriStr) {
return 'self';
}
// TODO(scheglov) Make it precise again, after Windows.
if (libraryUriStr.startsWith('file:')) {
return libraryUriStr.substring(libraryUriStr.lastIndexOf('/') + 1);
}
return libraryUriStr;
}
// Ignore the unit, skip to the library.
if (parent.name == '@unit') {
return _referenceToString(parent.parent!);
}
var name = reference.name;
if (name.isEmpty) {
fail('Currently every reference must have a name');
}
return '${_referenceToString(parent)}::$name';
}
String _stringOfSource(Source source) {
return '${source.uri}';
}
String _substitutionMapStr(Map<TypeParameterElement, DartType> map) {
var entriesStr = map.entries.map((entry) {
return '${entry.key.name}: ${_typeStr(entry.value)}';
}).join(', ');
return '{$entriesStr}';
}
String _typeStr(DartType type) {
return type.getDisplayString(withNullability: true);
}
void _writeAugmentationImportElement(AugmentationImportElement element) {
_sink.writeln('AugmentationImportElement');
_sink.withIndent(() {
_sink.writeWithIndent('uri: ');
writeDirectiveUri(element.uri);
});
}
void _writeLibraryExportElement(LibraryExportElement element) {
_sink.writeln('LibraryExportElement');
_sink.withIndent(() {
_sink.writeWithIndent('uri: ');
writeDirectiveUri(element.uri);
});
}
void _writeLibraryImportElement(LibraryImportElement element) {
_sink.writeln('LibraryImportElement');
_sink.withIndent(() {
_sink.writeWithIndent('uri: ');
writeDirectiveUri(element.uri);
});
}
void _writeMember(Member element) {
_sink.writeln(_nameOfMemberClass(element));
_sink.withIndent(() {
writeNamedElement('base', element.declaration);
if (element.isLegacy) {
_sink.writelnWithIndent('isLegacy: true');
}
var map = element.substitution.map;
if (map.isNotEmpty) {
var mapStr = _substitutionMapStr(map);
_sink.writelnWithIndent('substitution: $mapStr');
}
if (_configuration.withRedirectedConstructors) {
if (element is ConstructorMember) {
final redirected = element.redirectedConstructor;
writeNamedElement('redirectedConstructor', redirected);
}
}
});
}
void _writePartElement(PartElement element) {
writeDirectiveUri(element.uri);
}
static String _nameOfMemberClass(Member member) {
return '${member.runtimeType}';
}
}
class ElementPrinterConfiguration {
bool withInterfaceTypeElements = false;
bool withRedirectedConstructors = false;
}