blob: 5625a7be294782c37c4a47abd73f2cc95f524d67 [file] [log] [blame]
// Copyright (c) 2020, 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/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/generated/element_type_provider.dart';
import 'package:meta/meta.dart';
class ElementDisplayStringBuilder {
final StringBuffer _buffer = StringBuffer();
final bool skipAllDynamicArguments;
final bool withNullability;
ElementDisplayStringBuilder({
@required this.skipAllDynamicArguments,
@required this.withNullability,
});
@override
String toString() {
return _buffer.toString();
}
void writeAbstractElement(ElementImpl element) {
if (element.name != null) {
_write(element.name);
} else {
_write('<unnamed $runtimeType>');
}
}
void writeClassElement(ClassElementImpl element) {
if (element.isAbstract) {
_write('abstract ');
}
_write('class ');
_write(element.displayName);
_writeTypeParameters(element.typeParameters);
_writeTypeIfNotObject(' extends ', element.supertype);
_writeTypesIfNotEmpty(' with ', element.mixins);
_writeTypesIfNotEmpty(' implements ', element.interfaces);
}
void writeCompilationUnitElement(CompilationUnitElementImpl element) {
var path = element.source?.fullName;
_write(path ?? '{compilation unit}');
}
void writeConstructorElement(ConstructorElement element) {
_writeType(element.returnType);
_write(' ');
_write(element.enclosingElement.displayName);
if (element.displayName.isNotEmpty) {
_write('.');
_write(element.displayName);
}
_writeFormalParameters(element.parameters, forElement: true);
}
void writeDynamicType() {
_write('dynamic');
}
void writeEnumElement(EnumElementImpl element) {
_write('enum ');
_write(element.displayName);
}
void writeExecutableElement(ExecutableElement element, String name) {
_writeType(element.returnType);
_write(' ');
_write(name);
if (element.kind != ElementKind.GETTER) {
_writeTypeParameters(element.typeParameters);
_writeFormalParameters(element.parameters, forElement: true);
}
}
void writeExportElement(ExportElementImpl element) {
_write('export ');
(element.exportedLibrary as LibraryElementImpl).appendTo(this);
}
void writeExtensionElement(ExtensionElementImpl element) {
_write('extension ');
_write(element.displayName ?? '(unnamed)');
_writeTypeParameters(element.typeParameters);
_writeTypeIfNotNull(' on ', element.extendedType);
}
void writeFormalParameter(ParameterElement element) {
if (element.isRequiredPositional) {
_writeWithoutDelimiters(element, forElement: true);
} else if (element.isOptionalPositional) {
_write('[');
_writeWithoutDelimiters(element, forElement: true);
_write(']');
} else if (element.isNamed) {
_write('{');
_writeWithoutDelimiters(element, forElement: true);
_write('}');
}
}
void writeFunctionType(FunctionType type) {
type = _uniqueTypeParameters(type);
_writeType(type.returnType);
_write(' Function');
_writeTypeParameters(type.typeFormals);
_writeFormalParameters(type.parameters, forElement: false);
_writeNullability(type.nullabilitySuffix);
}
void writeGenericFunctionTypeElement(GenericFunctionTypeElementImpl element) {
_writeType(element.returnType);
_write(' Function');
_writeTypeParameters(element.typeParameters);
_writeFormalParameters(element.parameters, forElement: true);
}
void writeGenericTypeAliasElement(GenericTypeAliasElementImpl element) {
_write('typedef ');
_write(element.displayName);
_writeTypeParameters(element.typeParameters);
_write(' = ');
element.function?.appendTo(this);
}
void writeImportElement(ImportElementImpl element) {
_write('import ');
(element.importedLibrary as LibraryElementImpl).appendTo(this);
}
void writeInterfaceType(InterfaceType type) {
_write(type.element.name);
_writeTypeArguments(type.typeArguments);
_writeNullability(type.nullabilitySuffix);
}
void writeMixinElement(MixinElementImpl element) {
_write('mixin ');
_write(element.displayName);
_writeTypeParameters(element.typeParameters);
_writeTypesIfNotEmpty(' on ', element.superclassConstraints);
_writeTypesIfNotEmpty(' implements ', element.interfaces);
}
void writeNeverType(NeverTypeImpl type) {
_write('Never');
_writeNullability(type.nullabilitySuffix);
}
void writePrefixElement(PrefixElementImpl element) {
_write('as ');
_write(element.displayName);
}
void writeTypeParameter(TypeParameterElement element) {
if (element is TypeParameterElementImpl) {
var variance = element.variance;
if (!element.isLegacyCovariant && variance != Variance.unrelated) {
_write(variance.toKeywordString());
_write(' ');
}
}
_write(element.displayName);
if (element.bound != null) {
_write(' extends ');
_writeType(element.bound);
}
}
void writeTypeParameterType(TypeParameterTypeImpl type) {
_write(type.element.displayName);
_writeNullability(type.nullabilitySuffix);
if (type.promotedBound != null) {
_write(' & ');
_writeType(type.promotedBound);
}
}
void writeUnknownInferredType() {
_write('_');
}
void writeVariableElement(VariableElement element) {
_writeType(element.type);
_write(' ');
_write(element.displayName);
}
void writeVoidType() {
_write('void');
}
void _write(String str) {
_buffer.write(str);
}
void _writeFormalParameters(
List<ParameterElement> parameters, {
@required bool forElement,
}) {
_write('(');
var lastKind = _WriteFormalParameterKind.requiredPositional;
var lastClose = '';
void openGroup(_WriteFormalParameterKind kind, String open, String close) {
if (lastKind != kind) {
_write(lastClose);
_write(open);
lastKind = kind;
lastClose = close;
}
}
for (var i = 0; i < parameters.length; i++) {
if (i != 0) {
_write(', ');
}
var parameter = parameters[i];
if (parameter.isRequiredPositional) {
openGroup(_WriteFormalParameterKind.requiredPositional, '', '');
} else if (parameter.isOptionalPositional) {
openGroup(_WriteFormalParameterKind.optionalPositional, '[', ']');
} else if (parameter.isNamed) {
openGroup(_WriteFormalParameterKind.named, '{', '}');
}
_writeWithoutDelimiters(parameter, forElement: forElement);
}
_write(lastClose);
_write(')');
}
void _writeNullability(NullabilitySuffix nullabilitySuffix) {
if (withNullability) {
switch (nullabilitySuffix) {
case NullabilitySuffix.question:
_write('?');
break;
case NullabilitySuffix.star:
_write('*');
break;
case NullabilitySuffix.none:
break;
}
}
}
void _writeType(DartType type) {
(type as TypeImpl).appendTo(this);
}
void _writeTypeArguments(List<DartType> typeArguments) {
if (typeArguments.isEmpty) {
return;
}
if (skipAllDynamicArguments) {
if (typeArguments.every((t) => t.isDynamic)) {
return;
}
}
_write('<');
for (var i = 0; i < typeArguments.length; i++) {
if (i != 0) {
_write(', ');
}
(typeArguments[i] as TypeImpl).appendTo(this);
}
_write('>');
}
void _writeTypeIfNotNull(String prefix, DartType type) {
if (type != null) {
_write(prefix);
_writeType(type);
}
}
void _writeTypeIfNotObject(String prefix, DartType type) {
if (type != null && !type.isDartCoreObject) {
_write(prefix);
_writeType(type);
}
}
void _writeTypeParameters(List<TypeParameterElement> elements) {
if (elements.isEmpty) return;
_write('<');
for (var i = 0; i < elements.length; i++) {
if (i != 0) {
_write(', ');
}
(elements[i] as TypeParameterElementImpl).appendTo(this);
}
_write('>');
}
void _writeTypes(List<DartType> types) {
for (var i = 0; i < types.length; i++) {
if (i != 0) {
_write(', ');
}
_writeType(types[i]);
}
}
void _writeTypesIfNotEmpty(String prefix, List<DartType> types) {
if (types.isNotEmpty) {
_write(prefix);
_writeTypes(types);
}
}
void _writeWithoutDelimiters(
ParameterElement element, {
@required bool forElement,
}) {
if (element.isRequiredNamed) {
_write('required ');
}
_writeType(element.type);
if (forElement || element.isNamed) {
_write(' ');
_write(element.displayName);
}
if (forElement && element.defaultValueCode != null) {
_write(' = ');
_write(element.defaultValueCode);
}
}
static FunctionType _uniqueTypeParameters(FunctionType type) {
if (type.typeFormals.isEmpty) {
return type;
}
var referencedTypeParameters = <TypeParameterElement>{};
void collectTypeParameters(DartType type) {
if (type is TypeParameterType) {
referencedTypeParameters.add(type.element);
} else if (type is FunctionType) {
for (var typeParameter in type.typeFormals) {
collectTypeParameters(typeParameter.bound);
}
for (var parameter in type.parameters) {
collectTypeParameters(parameter.type);
}
collectTypeParameters(type.returnType);
} else if (type is InterfaceType) {
for (var typeArgument in type.typeArguments) {
collectTypeParameters(typeArgument);
}
}
}
collectTypeParameters(type);
referencedTypeParameters.removeAll(type.typeFormals);
var namesToAvoid = <String>{};
for (var typeParameter in referencedTypeParameters) {
namesToAvoid.add(typeParameter.displayName);
}
var newTypeParameters = <TypeParameterElement>[];
for (var typeParameter in type.typeFormals) {
var name = typeParameter.name;
for (var counter = 0; !namesToAvoid.add(name); counter++) {
const unicodeSubscriptZero = 0x2080;
const unicodeZero = 0x30;
var subscript = String.fromCharCodes('$counter'.codeUnits.map((n) {
return unicodeSubscriptZero + (n - unicodeZero);
}));
name = typeParameter.name + subscript;
}
var newTypeParameter = TypeParameterElementImpl(name, -1);
newTypeParameter.bound = typeParameter.bound;
newTypeParameters.add(newTypeParameter);
ElementTypeProvider.current
.freshTypeParameterCreated(newTypeParameter, typeParameter);
}
return replaceTypeParameters(type, newTypeParameters);
}
}
enum _WriteFormalParameterKind { requiredPositional, optionalPositional, named }