blob: cb6ade26b18f9a2a8300ea5be79c36cb5e57e245 [file] [log] [blame]
// Copyright (c) 2019, 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:kernel/ast.dart';
import '../base/lookup_result.dart';
import '../base/messages.dart';
import '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/type_builder.dart';
import '../kernel_generator_impl.dart';
import '../source/source_loader.dart';
/// Helper methods to use in annotated tests.
/// Returns a canonical simple name for [member].
String getMemberName(Member member) {
if (member is Procedure && member.isSetter) return '${member.name.text}=';
return member.name.text;
}
/// Returns a canonical qualified name for [member].
String getQualifiedMemberName(Member member) {
if (member.enclosingClass != null) {
return '${member.enclosingClass!.name}.${getMemberName(member)}';
}
return getMemberName(member);
}
/// Returns the enclosing [Member] for [node].
Member getEnclosingMember(TreeNode? node) {
while (node is! Member) {
node = node!.parent;
}
return node;
}
/// Finds the first [Library] in [component] with the given import [uri].
///
/// If [required] is `true` an error is thrown if no library was found.
Library? lookupLibrary(Component component, Uri uri, {bool required = true}) {
for (Library library in component.libraries) {
if (library.importUri == uri) {
return library;
}
for (LibraryPart part in library.parts) {
if (library.fileUri.resolve(part.partUri) == uri) {
return library;
}
}
}
if (required) {
throw new ArgumentError("Library '$uri' not found.");
}
return null;
}
/// Finds the first [Class] in [library] with the given [className].
///
/// If [required] is `true` an error is thrown if no class was found.
Class? lookupClass(Library library, String className, {bool required = true}) {
for (Class cls in library.classes) {
if (cls.name == className) {
return cls;
}
}
if (required) {
throw new ArgumentError("Class '$className' not found in '$library'.");
}
return null;
}
/// Finds the first [Extension] in [library] with the given [extensionName].
///
/// If [required] is `true` an error is thrown if no extension was found.
Extension? lookupExtension(
Library library,
String extensionName, {
bool required = true,
}) {
for (Extension extension in library.extensions) {
if (extension.name == extensionName) {
return extension;
}
}
if (required) {
throw new ArgumentError(
"Extension '$extensionName' not found in '${library.importUri}'.",
);
}
return null;
}
/// Finds the first [ExtensionTypeDeclaration] in [library] with the given
/// [extensionTypeName].
///
/// If [required] is `true` an error is thrown if no extension type was found.
ExtensionTypeDeclaration? lookupExtensionTypeDeclaration(
Library library,
String extensionTypeName, {
bool required = true,
}) {
for (ExtensionTypeDeclaration extensionTypeDeclaration
in library.extensionTypeDeclarations) {
if (extensionTypeDeclaration.name == extensionTypeName) {
return extensionTypeDeclaration;
}
}
if (required) {
throw new ArgumentError(
"Extension type '$extensionTypeName' not found in "
"'${library.importUri}'.",
);
}
return null;
}
/// Finds the first [Member] in [library] with the given canonical simple
/// [memberName] as computed by [getMemberName].
///
/// If [required] is `true` an error is thrown if no member was found.
Member? lookupLibraryMember(
Library library,
String memberName, {
bool required = true,
}) {
for (Member member in library.members) {
if (getMemberName(member) == memberName) {
return member;
}
}
if (required) {
throw new ArgumentError("Member '$memberName' not found in '$library'.");
}
return null;
}
/// Finds the first [Member] in [cls] with the given canonical simple
/// [memberName] as computed by [getMemberName].
///
/// If [required] is `true` an error is thrown if no member was found.
Member? lookupClassMember(
Class cls,
String memberName, {
bool required = true,
}) {
for (Member member in cls.members) {
if (getMemberName(member) == memberName) {
return member;
}
}
if (required) {
throw new ArgumentError("Member '$memberName' not found in '$cls'.");
}
return null;
}
LibraryBuilder? lookupLibraryBuilder(
InternalCompilerResult compilerResult,
Library library, {
bool required = true,
}) {
SourceLoader loader = compilerResult.kernelTargetForTesting!.loader;
LibraryBuilder? builder = loader.lookupLoadedLibraryBuilder(
library.importUri,
);
if (builder == null && required) {
throw new ArgumentError("DeclarationBuilder for $library not found.");
}
return builder;
}
ClassBuilder? lookupClassBuilder(
InternalCompilerResult compilerResult,
Class cls, {
bool required = true,
}) {
LibraryBuilder libraryBuilder = lookupLibraryBuilder(
compilerResult,
cls.enclosingLibrary,
required: required,
)!;
ClassBuilder? clsBuilder =
libraryBuilder.libraryNameSpace.lookup(cls.name)?.getable
as ClassBuilder?;
if (clsBuilder == null && required) {
throw new ArgumentError("ClassBuilder for $cls not found.");
}
return clsBuilder;
}
ExtensionBuilder? lookupExtensionBuilder(
InternalCompilerResult compilerResult,
Extension extension, {
bool required = true,
}) {
LibraryBuilder libraryBuilder = lookupLibraryBuilder(
compilerResult,
extension.enclosingLibrary,
required: required,
)!;
ExtensionBuilder? extensionBuilder;
libraryBuilder.libraryNameSpace.forEachLocalExtension((
ExtensionBuilder builder,
) {
if (builder.extension == extension) {
extensionBuilder = builder;
}
});
if (extensionBuilder == null && required) {
throw new ArgumentError("ExtensionBuilder for $extension not found.");
}
return extensionBuilder;
}
ExtensionTypeDeclarationBuilder? lookupExtensionTypeDeclarationBuilder(
InternalCompilerResult compilerResult,
ExtensionTypeDeclaration extensionTypeDeclaration, {
bool required = true,
}) {
LibraryBuilder libraryBuilder = lookupLibraryBuilder(
compilerResult,
extensionTypeDeclaration.enclosingLibrary,
required: required,
)!;
ExtensionTypeDeclarationBuilder? extensionTypeDeclarationBuilder;
Builder? builder = libraryBuilder.libraryNameSpace
.lookup(extensionTypeDeclaration.name)
?.getable;
if (builder is ExtensionTypeDeclarationBuilder &&
builder.extensionTypeDeclaration == extensionTypeDeclaration) {
extensionTypeDeclarationBuilder = builder;
}
if (extensionTypeDeclarationBuilder == null && required) {
throw new ArgumentError(
"ExtensionTypeDeclarationBuilder for $extensionTypeDeclaration "
"not found.",
);
}
return extensionTypeDeclarationBuilder;
}
/// Look up the [MemberBuilder] for [member] through the [ClassBuilder] for
/// [cls] using [memberName] as its name.
MemberBuilder? lookupClassMemberBuilder(
InternalCompilerResult compilerResult,
Class cls,
Member member,
String memberName, {
bool required = true,
}) {
ClassBuilder? classBuilder = lookupClassBuilder(
compilerResult,
cls,
required: required,
);
MemberBuilder? memberBuilder;
if (classBuilder != null) {
if (member is Constructor || member is Procedure && member.isFactory) {
memberBuilder = classBuilder.nameSpace
.lookupConstructor(memberName)
?.getable;
} else {
bool isSetter = member is Procedure && member.isSetter;
LookupResult? result = classBuilder.nameSpace.lookup(memberName);
memberBuilder =
(isSetter ? result?.setable : result?.getable) as MemberBuilder?;
}
}
if (memberBuilder == null && required) {
throw new ArgumentError("MemberBuilder for $member not found.");
}
return memberBuilder;
}
MemberBuilder? lookupMemberBuilder(
InternalCompilerResult compilerResult,
Member member, {
bool required = true,
}) {
MemberBuilder? memberBuilder;
if (member.isExtensionMember) {
String memberName = member.name.text;
String extensionName = memberName.substring(0, memberName.indexOf('|'));
memberName = memberName.substring(extensionName.length + 1);
bool isSetter = member is Procedure && member.isSetter;
if (memberName.startsWith('set#')) {
memberName = memberName.substring(4);
isSetter = true;
} else if (memberName.startsWith('get#')) {
memberName = memberName.substring(4);
}
Extension extension = lookupExtension(
member.enclosingLibrary,
extensionName,
required: true,
)!;
memberBuilder = lookupExtensionMemberBuilder(
compilerResult,
extension,
member,
memberName,
isSetter: isSetter,
required: required,
);
} else if (member.isExtensionTypeMember) {
String memberName = member.name.text;
String extensionTypeName = memberName.substring(0, memberName.indexOf('|'));
memberName = memberName.substring(extensionTypeName.length + 1);
bool isConstructor = false;
bool isSetter = member is Procedure && member.isSetter;
if (memberName.startsWith('set#')) {
memberName = memberName.substring(4);
isSetter = true;
} else if (memberName.startsWith('get#')) {
memberName = memberName.substring(4);
} else if (memberName.startsWith('constructor#')) {
memberName = memberName.substring(12);
isConstructor = true;
}
ExtensionTypeDeclaration extensionType = lookupExtensionTypeDeclaration(
member.enclosingLibrary,
extensionTypeName,
required: true,
)!;
memberBuilder = lookupExtensionTypeMemberBuilder(
compilerResult,
extensionType,
member,
memberName,
isConstructor: isConstructor,
isSetter: isSetter,
required: required,
);
} else if (member.enclosingClass != null) {
memberBuilder = lookupClassMemberBuilder(
compilerResult,
member.enclosingClass!,
member,
member.name.text,
required: required,
);
} else {
LibraryBuilder libraryBuilder = lookupLibraryBuilder(
compilerResult,
member.enclosingLibrary,
required: required,
)!;
bool isSetter = member is Procedure && member.isSetter;
LookupResult? result = libraryBuilder.libraryNameSpace.lookup(
member.name.text,
);
memberBuilder =
(isSetter ? result?.setable : result?.getable) as MemberBuilder?;
}
if (memberBuilder == null && required) {
throw new ArgumentError("MemberBuilder for $member not found.");
}
return memberBuilder;
}
/// Look up the [MemberBuilder] for [member] through the [ExtensionBuilder] for
/// [extension] using [memberName] as its name.
MemberBuilder? lookupExtensionMemberBuilder(
InternalCompilerResult compilerResult,
Extension extension,
Member member,
String memberName, {
bool isSetter = false,
bool required = true,
}) {
ExtensionBuilder? extensionBuilder = lookupExtensionBuilder(
compilerResult,
extension,
required: required,
);
MemberBuilder? memberBuilder;
if (extensionBuilder != null) {
bool isSetter = member is Procedure && member.isSetter;
LookupResult? result = extensionBuilder.nameSpace.lookup(memberName);
memberBuilder =
(isSetter ? result?.setable : result?.getable) as MemberBuilder?;
}
if (memberBuilder == null && required) {
throw new ArgumentError("MemberBuilder for $member not found.");
}
return memberBuilder;
}
/// Look up the [MemberBuilder] for [member] through the [ExtensionBuilder] for
/// [extensionType] using [memberName] as its name.
MemberBuilder? lookupExtensionTypeMemberBuilder(
InternalCompilerResult compilerResult,
ExtensionTypeDeclaration extensionType,
Member member,
String memberName, {
bool isConstructor = false,
bool isSetter = false,
bool required = true,
}) {
ExtensionTypeDeclarationBuilder? extensionTypeBuilder =
lookupExtensionTypeDeclarationBuilder(
compilerResult,
extensionType,
required: required,
);
MemberBuilder? memberBuilder;
if (extensionTypeBuilder != null) {
if (isConstructor) {
memberBuilder = extensionTypeBuilder.nameSpace
.lookupConstructor(memberName)
?.getable;
} else {
bool isSetter = member is Procedure && member.isSetter;
LookupResult? result = extensionTypeBuilder.nameSpace.lookup(memberName);
memberBuilder =
(isSetter ? result?.setable : result?.getable) as MemberBuilder?;
}
}
if (memberBuilder == null && required) {
throw new ArgumentError("MemberBuilder for $member not found.");
}
return memberBuilder;
}
/// Returns a textual representation of the constant [node] to be used in
/// testing.
String constantToText(
Constant node, {
TypeRepresentation typeRepresentation = TypeRepresentation.legacy,
}) {
StringBuffer sb = new StringBuffer();
new ConstantToTextVisitor(sb, typeRepresentation).visit(node);
return sb.toString();
}
enum TypeRepresentation {
legacy,
explicit,
// The type representation is made match the non-nullable-by-default type
// display string from the analyzer.
analyzerNonNullableByDefault,
}
/// Returns a textual representation of the type [node] to be used in
/// testing.
String typeToText(
DartType node, [
TypeRepresentation typeRepresentation = TypeRepresentation.legacy,
]) {
StringBuffer sb = new StringBuffer();
new DartTypeToTextVisitor(sb, typeRepresentation).visit(node);
return sb.toString();
}
Set<Class> computeAllSuperclasses(Class node) {
Set<Class> set = <Class>{};
_getAllSuperclasses(node, set);
return set;
}
void _getAllSuperclasses(Class node, Set<Class> set) {
if (set.add(node)) {
if (node.supertype != null) {
_getAllSuperclasses(node.supertype!.classNode, set);
}
if (node.mixedInType != null) {
_getAllSuperclasses(node.mixedInType!.classNode, set);
}
for (Supertype interface in node.implementedTypes) {
_getAllSuperclasses(interface.classNode, set);
}
}
}
String supertypeToText(
Supertype node, [
TypeRepresentation typeRepresentation = TypeRepresentation.legacy,
]) {
StringBuffer sb = new StringBuffer();
sb.write(node.classNode.name);
if (node.typeArguments.isNotEmpty) {
sb.write('<');
new DartTypeToTextVisitor(
sb,
typeRepresentation,
).visitList(node.typeArguments);
sb.write('>');
}
return sb.toString();
}
class ConstantToTextVisitor implements ConstantVisitor<void> {
final StringBuffer sb;
final DartTypeToTextVisitor typeToText;
ConstantToTextVisitor(this.sb, TypeRepresentation typeRepresentation)
: typeToText = new DartTypeToTextVisitor(sb, typeRepresentation);
void visit(Constant node) => node.accept(this);
void visitList(Iterable<Constant> nodes) {
String comma = '';
for (Constant node in nodes) {
sb.write(comma);
visit(node);
comma = ',';
}
}
@override
void visitNullConstant(NullConstant node) {
sb.write('Null()');
}
@override
void visitBoolConstant(BoolConstant node) {
sb.write('Bool(${node.value})');
}
@override
void visitIntConstant(IntConstant node) {
sb.write('Int(${node.value})');
}
@override
void visitDoubleConstant(DoubleConstant node) {
sb.write('Double(${node.value})');
}
@override
void visitStringConstant(StringConstant node) {
sb.write('String(${node.value})');
}
@override
void visitSymbolConstant(SymbolConstant node) {
sb.write('Symbol(${node.name})');
}
@override
void visitMapConstant(MapConstant node) {
sb.write('Map<');
typeToText.visit(node.keyType);
sb.write(',');
typeToText.visit(node.valueType);
sb.write('>(');
String comma = '';
for (ConstantMapEntry entry in node.entries) {
sb.write(comma);
entry.key.accept(this);
sb.write(':');
entry.value.accept(this);
comma = ',';
}
sb.write(')');
}
@override
void visitListConstant(ListConstant node) {
sb.write('List<');
typeToText.visit(node.typeArgument);
sb.write('>(');
visitList(node.entries);
sb.write(')');
}
@override
void visitSetConstant(SetConstant node) {
sb.write('Set<');
typeToText.visit(node.typeArgument);
sb.write('>(');
visitList(node.entries);
sb.write(')');
}
@override
void visitRecordConstant(RecordConstant node) {
sb.write('Record(');
String comma = '';
for (Constant field in node.positional) {
sb.write(comma);
field.accept(this);
comma = ',';
}
if (node.named.isNotEmpty) {
sb.write(comma);
sb.write('{');
comma = '';
for (MapEntry<String, Constant> entry in node.named.entries) {
sb.write(comma);
sb.write('${entry.key}:');
entry.value.accept(this);
comma = ',';
}
sb.write('}');
}
sb.write(')');
}
@override
void visitInstanceConstant(InstanceConstant node) {
sb.write('Instance(');
sb.write(node.classNode.name);
if (node.typeArguments.isNotEmpty) {
sb.write('<');
typeToText.visitList(node.typeArguments);
sb.write('>');
}
if (node.fieldValues.isNotEmpty) {
sb.write(',{');
String comma = '';
for (MapEntry<Reference, Constant> entry in node.fieldValues.entries) {
sb.write(comma);
sb.write(getMemberName(entry.key.asField));
sb.write(':');
visit(entry.value);
comma = ',';
}
sb.write('}');
}
sb.write(')');
}
@override
void visitInstantiationConstant(InstantiationConstant node) {
sb.write('Instantiation(');
Constant tearOffConstant = node.tearOffConstant;
if (tearOffConstant is TearOffConstant) {
sb.write(getMemberName(tearOffConstant.target));
} else {
visit(tearOffConstant);
}
sb.write('<');
typeToText.visitList(node.types);
sb.write('>)');
}
@override
void visitTypedefTearOffConstant(TypedefTearOffConstant node) {
sb.write('TypedefTearOff(');
sb.write(getMemberName(node.tearOffConstant.target));
if (node.parameters.isNotEmpty) {
sb.write('<');
for (int i = 0; i < node.parameters.length; i++) {
if (i > 0) {
sb.write(',');
if (typeToText.typeRepresentation ==
TypeRepresentation.analyzerNonNullableByDefault) {
sb.write(' ');
}
}
StructuralParameter structuralParameter = node.parameters[i];
sb.write(structuralParameter.name);
DartType bound = structuralParameter.bound;
if (!(bound is InterfaceType && bound.classNode.name == 'Object')) {
sb.write(' extends ');
typeToText.visit(bound);
}
}
sb.write('>');
}
sb.write('<');
typeToText.visitList(node.types);
sb.write('>)');
}
@override
void visitStaticTearOffConstant(StaticTearOffConstant node) {
sb.write('Function(');
sb.write(getMemberName(node.target));
sb.write(')');
}
@override
void visitConstructorTearOffConstant(ConstructorTearOffConstant node) {
sb.write('Constructor(');
sb.write(getMemberName(node.target));
sb.write(')');
}
@override
void visitRedirectingFactoryTearOffConstant(
RedirectingFactoryTearOffConstant node,
) {
sb.write('RedirectingFactory(');
sb.write(getMemberName(node.target));
sb.write(')');
}
@override
void visitTypeLiteralConstant(TypeLiteralConstant node) {
sb.write('TypeLiteral(');
typeToText.visit(node.type);
sb.write(')');
}
@override
void visitUnevaluatedConstant(UnevaluatedConstant node) {
sb.write('Unevaluated()');
}
@override
bool visitAuxiliaryConstant(AuxiliaryConstant node) {
throw new UnsupportedError(
"Unsupported auxiliary constant ${node} (${node.runtimeType}).",
);
}
}
class DartTypeToTextVisitor implements DartTypeVisitor<void> {
final StringBuffer sb;
final TypeRepresentation typeRepresentation;
DartTypeToTextVisitor(this.sb, this.typeRepresentation);
String get commaText {
if (typeRepresentation == TypeRepresentation.analyzerNonNullableByDefault) {
return ', ';
} else {
return ',';
}
}
void visit(DartType node) => node.accept(this);
void visitList(Iterable<DartType> nodes) {
String comma = '';
for (DartType node in nodes) {
sb.write(comma);
visit(node);
comma = commaText;
}
}
@override
void visitAuxiliaryType(AuxiliaryType node) {
throw new UnsupportedError(
"Unsupported auxiliary type ${node} (${node.runtimeType}).",
);
}
@override
void visitInvalidType(InvalidType node) {
sb.write('<invalid>');
}
@override
void visitDynamicType(DynamicType node) {
sb.write('dynamic');
}
@override
void visitVoidType(VoidType node) {
sb.write('void');
}
@override
void visitNeverType(NeverType node) {
sb.write('Never');
if (node.nullability != Nullability.nonNullable) {
sb.write(nullabilityToText(node.nullability, typeRepresentation));
}
}
@override
void visitNullType(NullType node) {
sb.write('Null');
}
@override
void visitInterfaceType(InterfaceType node) {
sb.write(node.classNode.name);
if (node.typeArguments.isNotEmpty) {
sb.write('<');
visitList(node.typeArguments);
sb.write('>');
}
if (!isNull(node)) {
sb.write(nullabilityToText(node.nullability, typeRepresentation));
}
}
@override
void visitFutureOrType(FutureOrType node) {
sb.write('FutureOr<');
visit(node.typeArgument);
sb.write('>');
sb.write(nullabilityToText(node.declaredNullability, typeRepresentation));
}
@override
void visitFunctionType(FunctionType node) {
visit(node.returnType);
sb.write(' Function');
if (node.typeParameters.isNotEmpty) {
sb.write('<');
for (int i = 0; i < node.typeParameters.length; i++) {
if (i > 0) {
sb.write(',');
if (typeRepresentation ==
TypeRepresentation.analyzerNonNullableByDefault) {
sb.write(' ');
}
}
StructuralParameter typeParameter = node.typeParameters[i];
sb.write(typeParameter.name);
DartType bound = typeParameter.bound;
if (!(bound is InterfaceType && bound.classNode.name == 'Object')) {
sb.write(' extends ');
visit(bound);
}
}
sb.write('>');
}
sb.write('(');
String comma = '';
visitList(node.positionalParameters.take(node.requiredParameterCount));
if (node.requiredParameterCount > 0) {
comma = commaText;
}
if (node.requiredParameterCount < node.positionalParameters.length) {
sb.write(comma);
sb.write('[');
visitList(node.positionalParameters.skip(node.requiredParameterCount));
sb.write(']');
comma = commaText;
}
if (node.namedParameters.isNotEmpty) {
sb.write(comma);
sb.write('{');
comma = '';
for (NamedType namedParameter in node.namedParameters) {
sb.write(comma);
if (namedParameter.isRequired) {
sb.write('required ');
}
visit(namedParameter.type);
sb.write(' ');
sb.write(namedParameter.name);
comma = commaText;
}
sb.write('}');
}
sb.write(')');
sb.write(nullabilityToText(node.nullability, typeRepresentation));
}
@override
void visitRecordType(RecordType node) {
sb.write('(');
String comma = '';
visitList(node.positional);
if (node.positional.isNotEmpty) {
comma = commaText;
}
if (node.named.isNotEmpty) {
sb.write(comma);
sb.write('{');
comma = '';
for (NamedType namedType in node.named) {
sb.write(comma);
if (namedType.isRequired) {
sb.write('required ');
}
visit(namedType.type);
sb.write(' ');
sb.write(namedType.name);
comma = commaText;
}
sb.write('}');
}
sb.write(')');
sb.write(nullabilityToText(node.nullability, typeRepresentation));
}
@override
void visitTypeParameterType(TypeParameterType node) {
sb.write(node.parameter.name);
sb.write(nullabilityToText(node.nullability, typeRepresentation));
}
@override
void visitStructuralParameterType(StructuralParameterType node) {
sb.write(node.parameter.name);
sb.write(nullabilityToText(node.nullability, typeRepresentation));
}
@override
void visitIntersectionType(IntersectionType node) {
visit(node.left);
sb.write(' & ');
visit(node.right);
}
@override
void visitTypedefType(TypedefType node) {
sb.write(node.typedefNode.name);
if (node.typeArguments.isNotEmpty) {
sb.write('<');
visitList(node.typeArguments);
sb.write('>');
}
sb.write(nullabilityToText(node.nullability, typeRepresentation));
}
@override
void visitExtensionType(ExtensionType node) {
sb.write(node.extensionTypeDeclaration.name);
if (node.typeArguments.isNotEmpty) {
sb.write('<');
visitList(node.typeArguments);
sb.write('>');
}
sb.write(nullabilityToText(node.declaredNullability, typeRepresentation));
}
}
/// Returns `true` if [type] is `Object` from `dart:core`.
bool isObject(DartType type) {
return type is InterfaceType &&
type.classNode.name == 'Object' &&
'${type.classNode.enclosingLibrary.importUri}' == 'dart:core';
}
/// Returns `true` if [type] is `Null` from `dart:core`.
bool isNull(DartType type) => type is NullType;
/// Returns a textual representation of the [typeParameter] to be used in
/// testing.
String typeParameterToText(TypeParameter typeParameter) {
String name = typeParameter.name!;
if (!isObject(typeParameter.bound)) {
return '$name extends ${typeToText(typeParameter.bound)}';
}
return name;
}
/// Returns a textual representation of the [type] to be used in testing.
String typeBuilderToText(TypeBuilder type) {
StringBuffer sb = new StringBuffer();
type.printOn(sb);
return sb.toString();
}
/// Returns a textual representation of the [typeParameter] to be used in
/// testing.
String typeVariableBuilderToText(NominalParameterBuilder typeParameter) {
String name = typeParameter.name;
if (typeParameter.bound != null) {
return '$name extends ${typeBuilderToText(typeParameter.bound!)}';
}
return name;
}
/// Returns a textual representation of [errors] to be used in testing.
String errorsToText(List<FormattedMessage> errors, {bool useCodes = false}) {
if (useCodes) {
return errors.map((m) => m.code.name).join(',');
} else {
return errors.map((m) => m.problemMessage).join(',');
}
}
/// Returns a textual representation of [descriptor] to be used in testing.
List<String> extensionMethodDescriptorToText(
ExtensionMemberDescriptor descriptor,
) {
String descriptorToText(Reference reference, {required bool forTearOff}) {
StringBuffer sb = new StringBuffer();
if (descriptor.isStatic) {
sb.write('static ');
}
switch (descriptor.kind) {
case ExtensionMemberKind.Method:
if (forTearOff) {
sb.write('tearoff ');
}
break;
case ExtensionMemberKind.Getter:
sb.write('getter ');
break;
case ExtensionMemberKind.Setter:
sb.write('setter ');
break;
case ExtensionMemberKind.Operator:
sb.write('operator ');
break;
case ExtensionMemberKind.Field:
sb.write('field ');
break;
}
sb.write(descriptor.name.text);
sb.write('=');
Member member = reference.asMember;
String name = member.name.text;
if (member is Procedure && member.isSetter) {
sb.write('$name=');
} else {
sb.write(name);
}
return sb.toString();
}
return [
if (descriptor.memberReference != null)
descriptorToText(descriptor.memberReference!, forTearOff: false),
if (descriptor.tearOffReference != null)
descriptorToText(descriptor.tearOffReference!, forTearOff: true),
];
}
/// Returns a textual representation of [nullability] to be used in testing.
String nullabilityToText(
Nullability nullability,
TypeRepresentation typeRepresentation,
) {
switch (nullability) {
case Nullability.nonNullable:
switch (typeRepresentation) {
case TypeRepresentation.explicit:
case TypeRepresentation.legacy:
return '!';
case TypeRepresentation.analyzerNonNullableByDefault:
return '';
}
case Nullability.nullable:
return '?';
case Nullability.undetermined:
switch (typeRepresentation) {
case TypeRepresentation.analyzerNonNullableByDefault:
return '';
default:
return '%';
}
}
}