blob: 05d42f47876c63627080e6089bfdf4f62d68e30d [file] [log] [blame]
// Copyright (c) 2016, 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 'package:kernel/class_hierarchy.dart'
show ClassHierarchy, ClassHierarchyBase, ClassHierarchyMembers;
import 'package:kernel/core_types.dart';
import 'package:kernel/names.dart' show equalsName;
import 'package:kernel/reference_from_index.dart'
show IndexedClass, IndexedLibrary;
import 'package:kernel/src/bounds_checks.dart';
import 'package:kernel/src/types.dart' show Types;
import 'package:kernel/type_algebra.dart'
show
FreshTypeParameters,
Substitution,
getFreshTypeParameters,
substitute,
updateBoundNullabilities;
import 'package:kernel/type_environment.dart';
import '../base/lookup_result.dart';
import '../base/messages.dart';
import '../base/modifiers.dart';
import '../base/name_space.dart';
import '../base/problems.dart' show unexpected, unhandled, unimplemented;
import '../base/scope.dart';
import '../base/uri_offset.dart';
import '../builder/augmentation_iterator.dart';
import '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/formal_parameter_builder.dart';
import '../builder/library_builder.dart';
import '../builder/member_builder.dart';
import '../builder/method_builder.dart';
import '../builder/named_type_builder.dart';
import '../builder/never_type_declaration_builder.dart';
import '../builder/nullability_builder.dart';
import '../builder/property_builder.dart';
import '../builder/synthesized_type_builder.dart';
import '../builder/type_builder.dart';
import '../fragment/fragment.dart';
import '../kernel/body_builder_context.dart';
import '../kernel/hierarchy/hierarchy_builder.dart';
import '../kernel/hierarchy/hierarchy_node.dart';
import '../kernel/kernel_helper.dart';
import '../kernel/type_algorithms.dart';
import '../kernel/utils.dart' show compareProcedures;
import 'builder_factory.dart';
import 'class_declaration.dart';
import 'name_scheme.dart';
import 'source_builder_mixins.dart';
import 'source_constructor_builder.dart';
import 'source_factory_builder.dart';
import 'source_library_builder.dart';
import 'source_loader.dart';
import 'source_member_builder.dart';
import 'source_type_parameter_builder.dart';
import 'type_parameter_scope_builder.dart';
Class initializeClass(
List<SourceNominalParameterBuilder>? typeParameters,
String name,
Uri fileUri,
int startOffset,
int nameOffset,
int endOffset,
IndexedClass? indexedClass,
{required bool isAugmentation}) {
Class cls = new Class(
name: name,
typeParameters: SourceNominalParameterBuilder.typeParametersFromBuilders(
typeParameters),
// If the class is an augmentation class it shouldn't use the reference
// from index even when available.
// TODO(johnniwinther): Avoid creating [Class] so early in the builder
// that we end up creating unneeded nodes.
reference: isAugmentation ? null : indexedClass?.reference,
fileUri: fileUri);
if (cls.startFileOffset == TreeNode.noOffset) {
cls.startFileOffset = startOffset;
}
if (cls.fileOffset == TreeNode.noOffset) {
cls.fileOffset = nameOffset;
}
if (cls.fileEndOffset == TreeNode.noOffset) {
cls.fileEndOffset = endOffset;
}
return cls;
}
class SourceClassBuilder extends ClassBuilderImpl
implements
Comparable<SourceClassBuilder>,
ClassDeclarationBuilder,
SourceDeclarationBuilder {
@override
final SourceLibraryBuilder libraryBuilder;
final int nameOffset;
@override
final String name;
@override
final Uri fileUri;
final Modifiers _modifiers;
@override
final Class cls;
final DeclarationNameSpaceBuilder nameSpaceBuilder;
late final MutableDeclarationNameSpace _nameSpace;
late final List<SourceMemberBuilder> _constructorBuilders;
late final List<SourceMemberBuilder> _memberBuilders;
@override
List<SourceNominalParameterBuilder>? typeParameters;
/// The scope in which the [typeParameters] are declared.
final LookupScope typeParameterScope;
TypeBuilder? _supertypeBuilder;
List<TypeBuilder>? _interfaceBuilders;
TypeBuilder? _mixedInTypeBuilder;
final IndexedClass? indexedClass;
final ClassDeclaration _introductory;
List<ClassDeclaration> _augmentations;
SourceClassBuilder(
{required Modifiers modifiers,
required this.name,
required this.typeParameters,
required this.typeParameterScope,
required this.nameSpaceBuilder,
required this.libraryBuilder,
required this.fileUri,
required this.nameOffset,
this.indexedClass,
TypeBuilder? mixedInTypeBuilder,
required ClassDeclaration introductory,
List<ClassDeclaration> augmentations = const []})
: _modifiers = modifiers,
_introductory = introductory,
_augmentations = augmentations,
_mixedInTypeBuilder = mixedInTypeBuilder,
cls = initializeClass(
typeParameters,
name,
fileUri,
introductory.startOffset,
introductory.nameOffset,
introductory.endOffset,
indexedClass,
isAugmentation: modifiers.isAugment) {
cls.hasConstConstructor = declaresConstConstructor;
}
@override
Iterator<SourceMemberBuilder> get unfilteredMembersIterator =>
_memberBuilders.iterator;
@override
Iterator<T> filteredMembersIterator<T extends MemberBuilder>(
{required bool includeDuplicates}) =>
new FilteredIterator<T>(_memberBuilders.iterator,
includeDuplicates: includeDuplicates);
@override
Iterator<SourceMemberBuilder> get unfilteredConstructorsIterator =>
_constructorBuilders.iterator;
@override
Iterator<T> filteredConstructorsIterator<T extends MemberBuilder>(
{required bool includeDuplicates}) =>
new FilteredIterator<T>(_constructorBuilders.iterator,
includeDuplicates: includeDuplicates);
void addMemberInternal(SourceMemberBuilder memberBuilder,
{required bool addToNameSpace}) {
if (addToNameSpace) {
_nameSpace.addLocalMember(memberBuilder.name, memberBuilder,
setter: false);
}
_memberBuilders.add(memberBuilder);
}
void addConstructorInternal(SourceMemberBuilder constructorBuilder,
{required bool addToNameSpace}) {
if (addToNameSpace) {
_nameSpace.addConstructor(constructorBuilder.name, constructorBuilder);
}
_constructorBuilders.add(constructorBuilder);
}
@override
int resolveConstructors(SourceLibraryBuilder libraryBuilder) {
int count = _introductory.resolveConstructorReferences(libraryBuilder);
for (ClassDeclaration augmentation in _augmentations) {
count += augmentation.resolveConstructorReferences(libraryBuilder);
}
if (count > 0) {
Iterator<SourceFactoryBuilder> iterator =
filteredConstructorsIterator(includeDuplicates: true);
while (iterator.moveNext()) {
SourceFactoryBuilder factoryBuilder = iterator.current;
assert(factoryBuilder.declarationBuilder == this,
"Unexpected builder $factoryBuilder in $this.");
factoryBuilder.resolveRedirectingFactory();
}
}
return count;
}
@override
int get fileOffset => nameOffset;
@override
bool get isAbstract => _modifiers.isAbstract;
@override
bool get isNamedMixinApplication {
return isMixinApplication && _modifiers.isNamedMixinApplication;
}
@override
bool get declaresConstConstructor => _modifiers.declaresConstConstructor;
@override
bool get isSealed => _modifiers.isSealed;
@override
bool get isBase => _modifiers.isBase;
@override
bool get isInterface => _modifiers.isInterface;
@override
bool get isFinal => _modifiers.isFinal;
/// Set to `true` if this class is declared using the `augment` modifier.
bool get isAugmentation => _modifiers.isAugment;
@override
bool get isMixinClass => _modifiers.isMixin;
@override
// Coverage-ignore(suite): Not run.
bool get isStatic => _modifiers.isStatic;
@override
bool get isMixinDeclaration => _introductory.isMixinDeclaration;
@override
DeclarationNameSpace get nameSpace => _nameSpace;
@override
void buildScopes(LibraryBuilder coreLibrary) {
_constructorBuilders = [];
_memberBuilders = [];
_nameSpace = nameSpaceBuilder.buildNameSpace(
loader: libraryBuilder.loader,
problemReporting: libraryBuilder,
enclosingLibraryBuilder: libraryBuilder,
declarationBuilder: this,
indexedLibrary: libraryBuilder.indexedLibrary,
indexedContainer: indexedClass,
containerType: ContainerType.Class,
containerName: new ClassName(name),
constructorBuilders: _constructorBuilders,
memberBuilders: _memberBuilders);
}
bool _hasComputedSupertypes = false;
void computeSupertypeBuilder({
required SourceLoader loader,
required ProblemReporting problemReporting,
required List<NominalParameterBuilder> unboundNominalParameters,
required IndexedLibrary? indexedLibrary,
required Map<SourceClassBuilder, TypeBuilder> mixinApplications,
required void Function(SourceClassBuilder) addAnonymousMixinClassBuilder,
}) {
assert(!_hasComputedSupertypes, "Supertypes have already been computed.");
_hasComputedSupertypes = true;
_supertypeBuilder = _applyMixins(
unboundNominalParameters: unboundNominalParameters,
compilationUnitScope: _introductory.compilationUnitScope,
problemReporting: problemReporting,
objectTypeBuilder: loader.target.objectType,
enclosingLibraryBuilder: libraryBuilder,
fileUri: _introductory.fileUri,
indexedLibrary: indexedLibrary,
supertype: _introductory.supertype,
mixins: _introductory.mixedInTypes,
mixinApplications: mixinApplications,
startOffset: _introductory.startOffset,
nameOffset: _introductory.nameOffset,
endOffset: _introductory.endOffset,
subclassName: _introductory.name,
isMixinDeclaration: _introductory.isMixinDeclaration,
typeParameters: typeParameters,
modifiers: Modifiers.empty,
onAnonymousMixin: (SourceClassBuilder anonymousMixinBuilder) {
Reference? reference = anonymousMixinBuilder.indexedClass?.reference;
if (reference != null) {
loader.buildersCreatedWithReferences[reference] =
anonymousMixinBuilder;
}
addAnonymousMixinClassBuilder(anonymousMixinBuilder);
anonymousMixinBuilder.buildScopes(loader.coreLibrary);
});
_interfaceBuilders = _introductory.interfaces;
}
void markAsCyclic(ClassBuilder objectClass) {
assert(_hasComputedSupertypes,
"Supertype of $this has not been computed yet.");
// Ensure that the cycle is broken by removing superclass and
// implemented interfaces.
cls.implementedTypes.clear();
cls.supertype = null;
cls.mixedInType = null;
_supertypeBuilder = new NamedTypeBuilderImpl.fromTypeDeclarationBuilder(
objectClass, const NullabilityBuilder.omitted(),
instanceTypeParameterAccess:
InstanceTypeParameterAccessState.Unexpected);
_interfaceBuilders = null;
_mixedInTypeBuilder = null;
// TODO(johnniwinther): Update the message for when a class depends on
// a cycle but does not depend on itself.
libraryBuilder.addProblem(
templateCyclicClassHierarchy.withArguments(fullNameForErrors),
fileOffset,
noLength,
fileUri);
}
// Coverage-ignore(suite): Not run.
/// Check that this class, which is the `Object` class, has no supertypes.
/// Recover by removing any found.
void checkObjectSupertypes() {
if (_supertypeBuilder != null) {
_supertypeBuilder = null;
libraryBuilder.addProblem(
messageObjectExtends, fileOffset, noLength, fileUri);
}
if (_interfaceBuilders != null) {
libraryBuilder.addProblem(
messageObjectImplements, fileOffset, noLength, fileUri);
_interfaceBuilders = null;
}
if (_mixedInTypeBuilder != null) {
libraryBuilder.addProblem(
messageObjectMixesIn, fileOffset, noLength, fileUri);
_mixedInTypeBuilder = null;
}
}
void installDefaultSupertypes(
ClassBuilder objectClassBuilder, Class objectClass) {
if (objectClass != cls) {
cls.supertype ??= objectClass.asRawSupertype;
_supertypeBuilder ??= new NamedTypeBuilderImpl.fromTypeDeclarationBuilder(
objectClassBuilder, const NullabilityBuilder.omitted(),
instanceTypeParameterAccess:
InstanceTypeParameterAccessState.Unexpected);
}
if (isMixinApplication) {
cls.mixedInType = mixedInTypeBuilder!.buildMixedInType(libraryBuilder);
}
}
@override
TypeBuilder? get supertypeBuilder {
assert(_hasComputedSupertypes,
"Supertype of $this has not been computed yet.");
return _supertypeBuilder;
}
@override
SourceLibraryBuilder get parent => libraryBuilder;
Class build(LibraryBuilder coreLibrary) {
void buildBuilders(SourceMemberBuilder memberBuilder) {
assert(memberBuilder.parent == this,
"Unexpected member $memberBuilder from outside $this.");
memberBuilder.buildOutlineNodes((
{required Member member,
Member? tearOff,
required BuiltMemberKind kind}) {
_addMemberToClass(memberBuilder, member);
if (tearOff != null) {
_addMemberToClass(memberBuilder, tearOff);
}
});
}
_memberBuilders.forEach(buildBuilders);
_constructorBuilders.forEach(buildBuilders);
if (_supertypeBuilder != null) {
_supertypeBuilder = _checkSupertype(_supertypeBuilder!);
}
TypeDeclarationBuilder? supertypeDeclaration =
supertypeBuilder?.computeUnaliasedDeclaration(isUsedAsClass: false);
if (LibraryBuilder.isFunction(supertypeDeclaration, coreLibrary)) {
_supertypeBuilder = null;
}
Supertype? supertype = supertypeBuilder?.buildSupertype(libraryBuilder,
isMixinDeclaration ? TypeUse.mixinOnType : TypeUse.classExtendsType);
if (!isMixinDeclaration &&
cls.supertype != null &&
// Coverage-ignore(suite): Not run.
cls.superclass!.isMixinDeclaration) {
// Coverage-ignore-block(suite): Not run.
// Declared mixins have interfaces that can be implemented, but they
// cannot be extended. However, a mixin declaration with a single
// superclass constraint is encoded with the constraint as the supertype,
// and that is allowed to be a mixin's interface.
libraryBuilder.addProblem(
templateSupertypeIsIllegal.withArguments(cls.superclass!.name),
fileOffset,
noLength,
fileUri);
supertype = null;
}
if (supertype == null && _supertypeBuilder is! NamedTypeBuilder) {
_supertypeBuilder = null;
}
cls.supertype = supertype;
if (_mixedInTypeBuilder != null) {
_mixedInTypeBuilder = _checkSupertype(_mixedInTypeBuilder!);
}
TypeDeclarationBuilder? mixedInDeclaration =
_mixedInTypeBuilder?.computeUnaliasedDeclaration(isUsedAsClass: false);
if (LibraryBuilder.isFunction(mixedInDeclaration, coreLibrary)) {
_mixedInTypeBuilder = null;
cls.isAnonymousMixin = false;
}
Supertype? mixedInType =
_mixedInTypeBuilder?.buildMixedInType(libraryBuilder);
cls.isMixinDeclaration = isMixinDeclaration;
cls.mixedInType = mixedInType;
// TODO(ahe): If `cls.supertype` is null, and this isn't Object, report a
// compile-time error.
cls.isAbstract = isAbstract;
cls.isMixinClass = isMixinClass;
cls.isSealed = isSealed;
cls.isBase = isBase;
cls.isInterface = isInterface;
cls.isFinal = isFinal;
List<TypeBuilder>? interfaceBuilders = this.interfaceBuilders;
if (interfaceBuilders != null) {
for (int i = 0; i < interfaceBuilders.length; ++i) {
interfaceBuilders[i] = _checkSupertype(interfaceBuilders[i]);
TypeDeclarationBuilder? implementedDeclaration = interfaceBuilders[i]
.computeUnaliasedDeclaration(isUsedAsClass: false);
if (LibraryBuilder.isFunction(implementedDeclaration, coreLibrary) &&
// Allow wasm to implement `Function`.
!libraryBuilder.mayImplementRestrictedTypes) {
continue;
}
Supertype? supertype = interfaceBuilders[i]
.buildSupertype(libraryBuilder, TypeUse.classImplementsType);
if (supertype != null) {
// TODO(ahe): Report an error if supertype is null.
cls.implementedTypes.add(supertype);
}
}
}
cls.procedures.sort(compareProcedures);
return cls;
}
@override
List<TypeBuilder>? get interfaceBuilders {
assert(_hasComputedSupertypes, "Interfaces have not been computed yet.");
return _interfaceBuilders;
}
@override
TypeBuilder? get mixedInTypeBuilder => _mixedInTypeBuilder;
void setInferredMixedInTypeArguments(List<TypeBuilder> typeArguments) {
InterfaceType mixedInType = cls.mixedInType!.asInterfaceType;
TypeBuilder mixedInTypeBuilder = _mixedInTypeBuilder!;
_mixedInTypeBuilder = new NamedTypeBuilderImpl.forDartType(
mixedInType,
mixedInTypeBuilder.declaration!,
new NullabilityBuilder.fromNullability(Nullability.nonNullable),
arguments: typeArguments,
fileUri: mixedInTypeBuilder.fileUri,
charOffset: mixedInTypeBuilder.charOffset);
libraryBuilder.registerBoundsCheck(mixedInType, mixedInTypeBuilder.fileUri!,
mixedInTypeBuilder.charOffset!, TypeUse.classWithType,
inferred: true);
}
BodyBuilderContext createBodyBuilderContext() {
return new ClassBodyBuilderContext(this);
}
void buildOutlineExpressions(ClassHierarchy classHierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
void build(Builder declaration) {
SourceMemberBuilder member = declaration as SourceMemberBuilder;
member.buildOutlineExpressions(
classHierarchy, delayedDefaultValueCloners);
}
BodyBuilderContext bodyBuilderContext = createBodyBuilderContext();
_introductory.buildOutlineExpressions(
annotatable: cls,
annotatableFileUri: cls.fileUri,
bodyBuilderContext: bodyBuilderContext,
libraryBuilder: libraryBuilder,
classHierarchy: classHierarchy,
createFileUriExpression: false);
for (ClassDeclaration augmentation in _augmentations) {
augmentation.buildOutlineExpressions(
annotatable: cls,
annotatableFileUri: cls.fileUri,
bodyBuilderContext: bodyBuilderContext,
classHierarchy: classHierarchy,
libraryBuilder: libraryBuilder,
createFileUriExpression: true);
}
if (typeParameters != null) {
for (int i = 0; i < typeParameters!.length; i++) {
typeParameters![i].buildOutlineExpressions(
libraryBuilder, bodyBuilderContext, classHierarchy);
}
}
filteredConstructorsIterator(includeDuplicates: false).forEach(build);
filteredMembersIterator(includeDuplicates: false).forEach(build);
}
/// Looks up the constructor by [name] on the class built by this class
/// builder.
SourceConstructorBuilder? lookupConstructor(Name name) {
if (name.text == "new") {
name = new Name("", name.library);
}
Builder? builder = nameSpace.lookupConstructor(name.text);
if (builder is SourceConstructorBuilder) {
return builder;
}
return null;
}
/// Looks up the super constructor by [name] on the superclass of the class
/// built by this class builder.
Constructor? lookupSuperConstructor(Name name) {
if (name.text == "new") {
name = new Name("", name.library);
}
Class? superclass = cls.superclass;
if (superclass != null) {
for (Constructor constructor in superclass.constructors) {
if (constructor.name == name) {
return constructor;
}
}
}
return null;
}
@override
int get typeParametersCount => typeParameters?.length ?? 0;
@override
List<DartType> buildAliasedTypeArguments(LibraryBuilder library,
List<TypeBuilder>? arguments, ClassHierarchyBase? hierarchy) {
if (arguments == null && typeParameters == null) {
return <DartType>[];
}
if (arguments == null && typeParameters != null) {
// TODO(johnniwinther): Use i2b here when needed.
List<DartType> result = new List<DartType>.generate(
typeParameters!.length,
(int i) => typeParameters![i]
.defaultType!
// TODO(johnniwinther): Using [libraryBuilder] here instead of
// [library] preserves the nullability of the original
// declaration. Should we legacy erase this?
.buildAliased(
libraryBuilder, TypeUse.defaultTypeAsTypeArgument, hierarchy),
growable: true);
return result;
}
if (arguments != null && arguments.length != typeParametersCount) {
// Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"SourceClassBuilder.buildAliasedTypeArguments: "
"the numbers of type parameters and type arguments don't match.",
expectedPhase: CompilationPhaseForProblemReporting.outline));
return unhandled(
templateTypeArgumentMismatch
.withArguments(typeParametersCount)
.problemMessage,
"buildTypeArguments",
-1,
null);
}
assert(arguments!.length == typeParametersCount);
List<DartType> result = new List<DartType>.generate(
arguments!.length,
(int i) =>
arguments[i].buildAliased(library, TypeUse.typeArgument, hierarchy),
growable: true);
return result;
}
/// Returns a map which maps the type parameters of [superclass] to their
/// respective values as defined by the superclass clause of this class (and
/// its superclasses).
///
/// It's assumed that [superclass] is a superclass of this class.
///
/// For example, given:
///
/// class Box<T> {}
/// class BeatBox extends Box<Beat> {}
/// class Beat {}
///
/// We have:
///
/// [[BeatBox]].getSubstitutionMap([[Box]]) -> {[[Box::T]]: Beat]]}.
///
/// It's an error if [superclass] isn't a superclass.
Map<TypeParameter, DartType> getSubstitutionMap(Class superclass) {
Supertype? supertype = cls.supertype;
Map<TypeParameter, DartType> substitutionMap = <TypeParameter, DartType>{};
List<DartType> arguments;
List<TypeParameter> variables;
Class? classNode;
while (classNode != superclass) {
classNode = supertype!.classNode;
arguments = supertype.typeArguments;
variables = classNode.typeParameters;
supertype = classNode.supertype;
if (variables.isNotEmpty) {
Map<TypeParameter, DartType> directSubstitutionMap =
<TypeParameter, DartType>{};
for (int i = 0; i < variables.length; i++) {
DartType argument =
i < arguments.length ? arguments[i] : const DynamicType();
// TODO(ahe): Investigate if requiring the caller to use
// `substituteDeep` from `package:kernel/type_algebra.dart` instead
// of `substitute` is faster. If so, we can simply this code.
argument = substitute(argument, substitutionMap);
directSubstitutionMap[variables[i]] = argument;
}
substitutionMap = directSubstitutionMap;
}
}
return substitutionMap;
}
void checkSupertypes(
CoreTypes coreTypes,
ClassHierarchyBuilder hierarchyBuilder,
Class objectClass,
Class enumClass,
Class underscoreEnumClass) {
// This method determines whether the class (that's being built) its super
// class appears both in 'extends' and 'implements' clauses and whether any
// interface appears multiple times in the 'implements' clause.
// Moreover, it checks that `FutureOr` and `void` are not among the
// supertypes and that `Enum` is not implemented by non-abstract classes.
// Anonymous mixins have to propagate certain class modifiers.
if (cls.isAnonymousMixin) {
Class? superclass = cls.superclass;
Class? mixedInClass = cls.mixedInClass;
// If either [superclass] or [mixedInClass] is sealed, the current
// anonymous mixin is sealed.
if (superclass != null && superclass.isSealed ||
mixedInClass != null && mixedInClass.isSealed) {
cls.isSealed = true;
} else {
// Otherwise, if either [superclass] or [mixedInClass] is base or final,
// then the current anonymous mixin is final.
bool superclassIsBaseOrFinal =
superclass != null && (superclass.isBase || superclass.isFinal);
bool mixedInClassIsBaseOrFinal = mixedInClass != null &&
(mixedInClass.isBase || mixedInClass.isFinal);
if (superclassIsBaseOrFinal || mixedInClassIsBaseOrFinal) {
cls.isFinal = true;
}
}
}
ClassHierarchyNode classHierarchyNode =
hierarchyBuilder.getNodeFromClass(cls);
if (libraryBuilder.libraryFeatures.enhancedEnums.isEnabled && !isEnum) {
bool hasEnumSuperinterface = false;
const List<String> restrictedNames = ["index", "hashCode", "=="];
Map<String, ClassBuilder> restrictedMembersInSuperclasses = {};
ClassBuilder? superclassDeclaringConcreteValues;
List<Supertype> interfaces = classHierarchyNode.superclasses;
for (int i = 0; !hasEnumSuperinterface && i < interfaces.length; i++) {
Class interfaceClass = interfaces[i].classNode;
if (interfaceClass == enumClass) {
hasEnumSuperinterface = true;
}
if (!interfaceClass.isEnum &&
interfaceClass != objectClass &&
interfaceClass != underscoreEnumClass) {
ClassHierarchyNode superclassHierarchyNode =
hierarchyBuilder.getNodeFromClass(interfaceClass);
for (String restrictedMemberName in restrictedNames) {
// TODO(johnniwinther): Handle injected members.
Builder? member = superclassHierarchyNode.classBuilder
.lookupLocalMember(restrictedMemberName, setter: false);
if (member is PropertyBuilder && !member.hasAbstractGetter ||
member is MethodBuilder && !member.isAbstract) {
restrictedMembersInSuperclasses[restrictedMemberName] ??=
superclassHierarchyNode.classBuilder;
}
}
Builder? member = superclassHierarchyNode.classBuilder
.lookupLocalMember("values", setter: false);
if (member is MemberBuilder &&
(member is PropertyBuilder && !member.hasAbstractGetter ||
// Coverage-ignore(suite): Not run.
member is MethodBuilder && !member.isAbstract)) {
superclassDeclaringConcreteValues ??= member.classBuilder;
}
}
}
interfaces = classHierarchyNode.interfaces;
for (int i = 0; !hasEnumSuperinterface && i < interfaces.length; i++) {
if (interfaces[i].classNode == enumClass) {
hasEnumSuperinterface = true;
}
}
if (!cls.isAbstract && !cls.isEnum && hasEnumSuperinterface) {
libraryBuilder.addProblem(
templateEnumSupertypeOfNonAbstractClass.withArguments(name),
fileOffset,
noLength,
fileUri);
}
if (hasEnumSuperinterface && cls != underscoreEnumClass) {
// Instance members named `values` are restricted.
LookupResult? result = nameSpace.lookupLocalMember("values");
NamedBuilder? customValuesDeclaration = result?.getable;
if (customValuesDeclaration != null &&
!customValuesDeclaration.isStatic) {
// Retrieve the earliest declaration for error reporting.
while (customValuesDeclaration?.next != null) {
// Coverage-ignore-block(suite): Not run.
customValuesDeclaration = customValuesDeclaration?.next;
}
Uri fileUri = customValuesDeclaration!.fileUri!;
int fileOffset = customValuesDeclaration.fileOffset;
int length = customValuesDeclaration.fullNameForErrors.length;
if (customValuesDeclaration is PropertyBuilder) {
UriOffsetLength uriOffset =
customValuesDeclaration.getterUriOffset!;
fileUri = uriOffset.fileUri;
fileOffset = uriOffset.fileOffset;
length = uriOffset.length;
}
libraryBuilder.addProblem(
templateEnumImplementerContainsValuesDeclaration
.withArguments(this.name),
fileOffset,
length,
fileUri);
}
customValuesDeclaration = result?.setable;
if (customValuesDeclaration != null &&
!customValuesDeclaration.isStatic) {
// Retrieve the earliest declaration for error reporting.
while (customValuesDeclaration?.next != null) {
// Coverage-ignore-block(suite): Not run.
customValuesDeclaration = customValuesDeclaration?.next;
}
Uri fileUri = customValuesDeclaration!.fileUri!;
int fileOffset = customValuesDeclaration.fileOffset;
int length = customValuesDeclaration.fullNameForErrors.length;
if (customValuesDeclaration is PropertyBuilder) {
UriOffsetLength uriOffset =
customValuesDeclaration.setterUriOffset!;
fileUri = uriOffset.fileUri;
fileOffset = uriOffset.fileOffset;
length = uriOffset.length;
}
libraryBuilder.addProblem(
templateEnumImplementerContainsValuesDeclaration
.withArguments(this.name),
fileOffset,
length,
fileUri);
}
if (superclassDeclaringConcreteValues != null) {
libraryBuilder.addProblem(
templateInheritedRestrictedMemberOfEnumImplementer.withArguments(
"values", superclassDeclaringConcreteValues.name),
fileOffset,
noLength,
fileUri);
}
// Non-setter concrete instance members named `index` and hashCode and
// operator == are restricted.
for (String restrictedMemberName in restrictedNames) {
Builder? member =
nameSpace.lookupLocalMember(restrictedMemberName)?.getable;
if (member is MemberBuilder &&
(member is PropertyBuilder && !member.hasAbstractGetter ||
member is MethodBuilder && !member.isAbstract)) {
libraryBuilder.addProblem(
templateEnumImplementerContainsRestrictedInstanceDeclaration
.withArguments(this.name, restrictedMemberName),
member.fileOffset,
member.fullNameForErrors.length,
fileUri);
}
if (restrictedMembersInSuperclasses
.containsKey(restrictedMemberName)) {
ClassBuilder restrictedNameMemberProvider =
restrictedMembersInSuperclasses[restrictedMemberName]!;
libraryBuilder.addProblem(
templateInheritedRestrictedMemberOfEnumImplementer
.withArguments(restrictedMemberName,
restrictedNameMemberProvider.name),
fileOffset,
noLength,
fileUri);
}
}
}
}
void fail(TypeBuilder target, Message message,
TypeDeclarationBuilder? aliasBuilder) {
int nameOffset = target.typeName!.nameOffset;
int nameLength = target.typeName!.nameLength;
if (aliasBuilder is TypeAliasBuilder) {
// Coverage-ignore-block(suite): Not run.
libraryBuilder.addProblem(
message, nameOffset, nameLength, target.fileUri,
context: [
messageTypedefCause.withLocation(
aliasBuilder.fileUri, aliasBuilder.fileOffset, noLength),
]);
} else {
libraryBuilder.addProblem(
message, nameOffset, nameLength, target.fileUri);
}
}
// Extract and check superclass (if it exists).
ClassBuilder? superClass;
TypeBuilder? superClassType = supertypeBuilder;
if (superClassType != null) {
TypeDeclarationBuilder? superDeclaration = superClassType.declaration;
TypeDeclarationBuilder? unaliasedSuperDeclaration =
superClassType.computeUnaliasedDeclaration(isUsedAsClass: true);
// TODO(eernst): Should gather 'restricted supertype' checks in one place,
// e.g., dynamic/int/String/Null and more are checked elsewhere.
if (unaliasedSuperDeclaration is NeverTypeDeclarationBuilder) {
fail(superClassType, messageExtendsNever, superDeclaration);
} else if (unaliasedSuperDeclaration is ClassBuilder) {
superClass = unaliasedSuperDeclaration;
}
}
if (cls.isMixinClass) {
// Check that the class does not have a constructor.
Iterator<SourceMemberBuilder> constructorIterator =
filteredConstructorsIterator(includeDuplicates: false);
while (constructorIterator.moveNext()) {
SourceMemberBuilder constructor = constructorIterator.current;
// Assumes the constructor isn't synthetic since
// [installSyntheticConstructors] hasn't been called yet.
if (constructor is SourceConstructorBuilderImpl) {
// Report an error if a mixin class has a constructor with parameters,
// is external, or is a redirecting constructor.
if (constructor.isRedirecting ||
constructor.hasParameters ||
constructor.isExternal) {
libraryBuilder.addProblem(
templateIllegalMixinDueToConstructors
.withArguments(fullNameForErrors),
constructor.fileOffset,
noLength,
constructor.fileUri);
}
}
}
// Check that the class has 'Object' as their superclass.
if (superClass != null &&
superClassType != null &&
superClass.cls != objectClass) {
libraryBuilder.addProblem(
templateMixinInheritsFromNotObject.withArguments(name),
superClassType.charOffset ?? TreeNode.noOffset,
noLength,
superClassType.fileUri ?? // Coverage-ignore(suite): Not run.
fileUri);
}
}
if (classHierarchyNode.isMixinApplication) {
assert(_mixedInTypeBuilder != null,
"No mixed in type builder for mixin application $this.");
ClassHierarchyNode mixedInNode = classHierarchyNode.mixedInNode!;
ClassHierarchyNode? mixinSuperClassNode =
mixedInNode.directSuperClassNode;
if (mixinSuperClassNode != null &&
mixinSuperClassNode.classBuilder.cls != objectClass &&
!mixedInNode.classBuilder.cls.isMixinDeclaration) {
libraryBuilder.addProblem(
templateMixinInheritsFromNotObject
.withArguments(mixedInNode.classBuilder.name),
_mixedInTypeBuilder!.charOffset ?? TreeNode.noOffset,
noLength,
_mixedInTypeBuilder!.fileUri ?? // Coverage-ignore(suite): Not run.
fileUri,
);
}
}
if (interfaceBuilders == null) return;
// Validate interfaces.
Map<ClassBuilder, int>? problems;
Map<ClassBuilder, int>? problemsOffsets;
Set<ClassBuilder> implemented = new Set<ClassBuilder>();
for (TypeBuilder type in interfaceBuilders!) {
TypeDeclarationBuilder? typeDeclaration = type.declaration;
TypeDeclarationBuilder? unaliasedDeclaration =
type.computeUnaliasedDeclaration(isUsedAsClass: true);
if (unaliasedDeclaration is ClassBuilder) {
ClassBuilder interface = unaliasedDeclaration;
if (superClass == interface) {
libraryBuilder.addProblem(
templateImplementsSuperClass.withArguments(interface.name),
this.fileOffset,
noLength,
this.fileUri);
} else if (interface.cls.name == "FutureOr" &&
// Coverage-ignore(suite): Not run.
interface.cls.enclosingLibrary.importUri.isScheme("dart") &&
// Coverage-ignore(suite): Not run.
interface.cls.enclosingLibrary.importUri.path == "async") {
// Coverage-ignore-block(suite): Not run.
libraryBuilder.addProblem(messageImplementsFutureOr, this.fileOffset,
noLength, this.fileUri);
} else if (implemented.contains(interface)) {
// Aggregate repetitions.
problems ??= <ClassBuilder, int>{};
problems[interface] ??= 0;
problems[interface] = problems[interface]! + 1;
problemsOffsets ??= <ClassBuilder, int>{};
problemsOffsets[interface] ??= type.charOffset ?? TreeNode.noOffset;
} else {
implemented.add(interface);
}
}
if (unaliasedDeclaration != superClass) {
// TODO(eernst): Have all 'restricted supertype' checks in one place.
if (unaliasedDeclaration is NeverTypeDeclarationBuilder) {
fail(type, messageImplementsNever, typeDeclaration);
}
}
}
if (problems != null) {
problems.forEach((ClassBuilder interface, int repetitions) {
libraryBuilder.addProblem(
templateImplementsRepeated.withArguments(
interface.name, repetitions),
problemsOffsets![interface]!,
noLength,
fileUri);
});
}
}
void checkMixinApplication(ClassHierarchy hierarchy, CoreTypes coreTypes) {
TypeEnvironment typeEnvironment = new TypeEnvironment(coreTypes, hierarchy);
// A mixin declaration can only be applied to a class that implements all
// the declaration's superclass constraints.
InterfaceType supertype = cls.supertype!.asInterfaceType;
Substitution substitution = Substitution.fromSupertype(cls.mixedInType!);
for (Supertype constraint in cls.mixedInClass!.onClause) {
InterfaceType requiredInterface =
substitution.substituteSupertype(constraint).asInterfaceType;
InterfaceType? implementedInterface =
hierarchy.getInterfaceTypeAsInstanceOfClass(
supertype, requiredInterface.classNode);
if (implementedInterface == null ||
!typeEnvironment.areMutualSubtypes(implementedInterface,
requiredInterface, SubtypeCheckMode.withNullabilities)) {
libraryBuilder.addProblem(
templateMixinApplicationIncompatibleSupertype.withArguments(
supertype, requiredInterface, cls.mixedInType!.asInterfaceType),
cls.fileOffset,
noLength,
cls.fileUri);
}
}
}
void checkRedirectingFactories(TypeEnvironment typeEnvironment) {
Iterator<SourceFactoryBuilder> iterator =
filteredConstructorsIterator(includeDuplicates: true);
while (iterator.moveNext()) {
iterator.current.checkRedirectingFactories(typeEnvironment);
}
}
Map<String, ConstructorRedirection>? _redirectingConstructors;
/// Registers a constructor redirection for this class and returns true if
/// this redirection gives rise to a cycle that has not been reported before.
bool checkConstructorCyclic(String source, String target) {
ConstructorRedirection? redirect = new ConstructorRedirection(target);
_redirectingConstructors ??= <String, ConstructorRedirection>{};
_redirectingConstructors![source] = redirect;
while (redirect != null) {
if (redirect.cycleReported) return false;
if (redirect.target == source) {
redirect.cycleReported = true;
return true;
}
redirect = _redirectingConstructors![redirect.target];
}
return false;
}
TypeBuilder _checkSupertype(TypeBuilder supertype) {
if (typeParameters == null) return supertype;
Message? message;
for (int i = 0; i < typeParameters!.length; ++i) {
NominalParameterBuilder typeParameterBuilder = typeParameters![i];
Variance variance = supertype
.computeTypeParameterBuilderVariance(typeParameterBuilder,
sourceLoader: libraryBuilder.loader)
.variance!;
if (!variance.greaterThanOrEqual(typeParameters![i].variance)) {
if (typeParameters![i].parameter.isLegacyCovariant) {
message = templateInvalidTypeParameterInSupertype.withArguments(
typeParameters![i].name,
variance.keyword,
supertype.typeName!.name);
} else {
// Coverage-ignore-block(suite): Not run.
message =
templateInvalidTypeParameterInSupertypeWithVariance.withArguments(
typeParameters![i].variance.keyword,
typeParameters![i].name,
variance.keyword,
supertype.typeName!.name);
}
libraryBuilder.addProblem(message, fileOffset, noLength, fileUri);
}
}
if (message != null) {
TypeName typeName = supertype.typeName!;
return new NamedTypeBuilderImpl(
typeName, const NullabilityBuilder.omitted(),
fileUri: fileUri,
charOffset: fileOffset,
instanceTypeParameterAccess:
InstanceTypeParameterAccessState.Unexpected)
..bind(
libraryBuilder,
new InvalidTypeDeclarationBuilder(typeName.name,
message.withLocation(fileUri, fileOffset, noLength)));
}
return supertype;
}
void checkVarianceInField(TypeEnvironment typeEnvironment,
{required DartType fieldType,
required bool isInstanceMember,
required bool hasSetter,
required bool isCovariantByDeclaration,
required Uri fileUri,
required int fileOffset}) {
List<TypeParameter> typeParameters = cls.typeParameters;
if (typeParameters.isNotEmpty) {
for (TypeParameter typeParameter in typeParameters) {
Variance fieldVariance = computeVariance(typeParameter, fieldType);
if (isInstanceMember) {
reportVariancePositionIfInvalid(
fieldVariance, typeParameter, fileUri, fileOffset);
}
if (isInstanceMember && hasSetter && !isCovariantByDeclaration) {
fieldVariance = Variance.contravariant.combine(fieldVariance);
reportVariancePositionIfInvalid(
fieldVariance, typeParameter, fileUri, fileOffset);
}
}
}
}
void checkVarianceInTypeParameters(TypeEnvironment typeEnvironment,
List<SourceNominalParameterBuilder>? typeParameters) {
List<TypeParameter> classTypeParameters = cls.typeParameters;
if (typeParameters != null && classTypeParameters.isNotEmpty) {
for (NominalParameterBuilder nominalParameter in typeParameters) {
for (TypeParameter classTypeParameter in classTypeParameters) {
Variance typeVariance = Variance.invariant.combine(computeVariance(
classTypeParameter, nominalParameter.parameter.bound));
reportVariancePositionIfInvalid(typeVariance, classTypeParameter,
fileUri, nominalParameter.fileOffset);
}
}
}
}
void checkVarianceInFormals(
TypeEnvironment type, List<FormalParameterBuilder>? formals) {
List<TypeParameter> classTypeParameters = cls.typeParameters;
if (formals != null && classTypeParameters.isNotEmpty) {
for (FormalParameterBuilder formal in formals) {
if (!formal.isCovariantByDeclaration) {
for (TypeParameter typeParameter in classTypeParameters) {
Variance formalVariance = Variance.contravariant
.combine(computeVariance(typeParameter, formal.variable!.type));
reportVariancePositionIfInvalid(formalVariance, typeParameter,
formal.fileUri, formal.fileOffset);
}
}
}
}
}
void checkVarianceInReturnType(TypeEnvironment type, DartType returnType,
{required Uri fileUri, required fileOffset}) {
List<TypeParameter> classTypeParameters = cls.typeParameters;
if (classTypeParameters.isNotEmpty) {
for (TypeParameter typeParameter in classTypeParameters) {
Variance returnTypeVariance =
computeVariance(typeParameter, returnType);
reportVariancePositionIfInvalid(
returnTypeVariance, typeParameter, fileUri, fileOffset,
isReturnType: true);
}
}
}
// Coverage-ignore(suite): Not run.
void checkVarianceInFunction(Procedure procedure,
TypeEnvironment typeEnvironment, List<TypeParameter> typeParameters) {
List<TypeParameter> functionTypeParameters =
procedure.function.typeParameters;
List<VariableDeclaration> positionalParameters =
procedure.function.positionalParameters;
List<VariableDeclaration> namedParameters =
procedure.function.namedParameters;
DartType returnType = procedure.function.returnType;
for (TypeParameter functionParameter in functionTypeParameters) {
for (TypeParameter typeParameter in typeParameters) {
Variance typeVariance = Variance.invariant
.combine(computeVariance(typeParameter, functionParameter.bound));
reportVariancePositionIfInvalid(
typeVariance, typeParameter, fileUri, functionParameter.fileOffset);
}
}
for (VariableDeclaration formal in positionalParameters) {
if (!formal.isCovariantByDeclaration) {
for (TypeParameter typeParameter in typeParameters) {
Variance formalVariance = Variance.contravariant
.combine(computeVariance(typeParameter, formal.type));
reportVariancePositionIfInvalid(
formalVariance, typeParameter, fileUri, formal.fileOffset);
}
}
}
for (VariableDeclaration named in namedParameters) {
for (TypeParameter typeParameter in typeParameters) {
Variance namedVariance = Variance.contravariant
.combine(computeVariance(typeParameter, named.type));
reportVariancePositionIfInvalid(
namedVariance, typeParameter, fileUri, named.fileOffset);
}
}
for (TypeParameter typeParameter in typeParameters) {
Variance returnTypeVariance = computeVariance(typeParameter, returnType);
reportVariancePositionIfInvalid(returnTypeVariance, typeParameter,
fileUri, procedure.function.fileOffset,
isReturnType: true);
}
}
void reportVariancePositionIfInvalid(Variance variance,
TypeParameter typeParameter, Uri fileUri, int fileOffset,
{bool isReturnType = false}) {
SourceLibraryBuilder library = this.libraryBuilder;
if (!typeParameter.isLegacyCovariant &&
!variance.greaterThanOrEqual(typeParameter.variance)) {
Message message;
if (isReturnType) {
message = templateInvalidTypeParameterVariancePositionInReturnType
.withArguments(typeParameter.variance.keyword, typeParameter.name!,
variance.keyword);
} else {
message = templateInvalidTypeParameterVariancePosition.withArguments(
typeParameter.variance.keyword,
typeParameter.name!,
variance.keyword);
}
library.reportTypeArgumentIssue(message, fileUri, fileOffset,
typeParameter: typeParameter);
}
}
@override
int computeDefaultTypes(ComputeDefaultTypeContext context) {
bool hasErrors = context.reportNonSimplicityIssues(this, typeParameters);
int count = context.computeDefaultTypesForVariables(typeParameters,
inErrorRecovery: hasErrors);
Iterator<SourceMemberBuilder> iterator =
filteredConstructorsIterator(includeDuplicates: false);
while (iterator.moveNext()) {
count += iterator.current
.computeDefaultTypes(context, inErrorRecovery: hasErrors);
}
Iterator<SourceMemberBuilder> memberIterator =
filteredMembersIterator(includeDuplicates: false);
while (memberIterator.moveNext()) {
count += memberIterator.current
.computeDefaultTypes(context, inErrorRecovery: hasErrors);
}
return count;
}
void checkTypesInOutline(TypeEnvironment typeEnvironment) {
Iterator<SourceMemberBuilder> memberIterator =
filteredMembersIterator(includeDuplicates: false);
while (memberIterator.moveNext()) {
SourceMemberBuilder builder = memberIterator.current;
builder.checkVariance(this, typeEnvironment);
builder.checkTypes(libraryBuilder, nameSpace, typeEnvironment);
}
Iterator<SourceMemberBuilder> constructorIterator =
filteredConstructorsIterator(includeDuplicates: false);
while (constructorIterator.moveNext()) {
SourceMemberBuilder builder = constructorIterator.current;
builder.checkTypes(libraryBuilder, nameSpace, typeEnvironment);
}
}
void addSyntheticConstructor(
SyntheticSourceConstructorBuilder constructorBuilder) {
String name = constructorBuilder.name;
assert(
nameSpace.lookupConstructor(name) == null,
"Unexpected existing constructor when adding synthetic constructor "
"$constructorBuilder to $this.");
addConstructorInternal(constructorBuilder, addToNameSpace: true);
// Synthetic constructors are created after the component has been built
// so we need to add the constructor to the class.
cls.addConstructor(constructorBuilder.invokeTarget);
if (constructorBuilder.readTarget != constructorBuilder.invokeTarget) {
cls.addProcedure(constructorBuilder.readTarget as Procedure);
}
if (constructorBuilder.isConst) {
cls.hasConstConstructor = true;
}
}
int buildBodyNodes() {
adjustAnnotationFileUri(cls, cls.fileUri);
int count = 0;
void buildMembers(SourceMemberBuilder builder) {
assert(builder.parent == this, "Unexpected member $builder in this.");
count += builder.buildBodyNodes(
// Coverage-ignore(suite): Not run.
(
{required Member member,
Member? tearOff,
required BuiltMemberKind kind}) {
_addMemberToClass(builder, member);
if (tearOff != null) {
_addMemberToClass(builder, tearOff);
}
});
}
unfilteredMembersIterator.forEach(buildMembers);
unfilteredConstructorsIterator.forEach(buildMembers);
return count;
}
void _addMemberToClass(SourceMemberBuilder memberBuilder, Member member) {
member.parent = cls;
if (!memberBuilder.isDuplicate) {
if (member is Procedure) {
cls.addProcedure(member);
} else if (member is Field) {
cls.addField(member);
} else if (member is Constructor) {
cls.addConstructor(member);
} else {
unhandled("${member.runtimeType}", "getMember", member.fileOffset,
member.fileUri);
}
}
}
/// Return a map whose keys are the supertypes of this [SourceClassBuilder]
/// after expansion of type aliases, if any. For each supertype key, the
/// corresponding value is the type alias which was unaliased in order to
/// find the supertype, or null if the supertype was not aliased.
Map<TypeDeclarationBuilder?, TypeAliasBuilder?> computeDirectSupertypes(
ClassBuilder objectClass) {
final Map<TypeDeclarationBuilder?, TypeAliasBuilder?> result = {};
final TypeBuilder? supertype = this.supertypeBuilder;
if (supertype != null) {
TypeDeclarationBuilder? declaration = supertype.declaration;
TypeDeclarationBuilder? unaliasedDeclaration =
supertype.computeUnaliasedDeclaration(isUsedAsClass: true);
result[unaliasedDeclaration] =
declaration is TypeAliasBuilder ? declaration : null;
} else if (objectClass != this) {
result[objectClass] = null;
}
final List<TypeBuilder>? interfaces = interfaceBuilders;
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
TypeBuilder interface = interfaces[i];
TypeDeclarationBuilder? declaration = interface.declaration;
TypeDeclarationBuilder? unaliasedDeclaration =
interface.computeUnaliasedDeclaration(isUsedAsClass: true);
result[unaliasedDeclaration] =
declaration is TypeAliasBuilder ? declaration : null;
}
}
final TypeBuilder? mixedInTypeBuilder = _mixedInTypeBuilder;
if (mixedInTypeBuilder != null) {
TypeDeclarationBuilder? declaration = mixedInTypeBuilder.declaration;
TypeDeclarationBuilder? unaliasedDeclaration =
mixedInTypeBuilder.computeUnaliasedDeclaration(isUsedAsClass: true);
result[unaliasedDeclaration] =
declaration is TypeAliasBuilder ? declaration : null;
}
return result;
}
@override
int compareTo(SourceClassBuilder other) {
int result = "$fileUri".compareTo("${other.fileUri}");
if (result != 0) return result;
return fileOffset.compareTo(other.fileOffset);
}
void _handleSeenCovariant(
ClassHierarchyMembers memberHierarchy,
Member interfaceMember,
bool isSetter,
callback(Member interfaceMember, bool isSetter)) {
// When a parameter is covariant we have to check that we also
// override the same member in all parents.
for (Supertype supertype in interfaceMember.enclosingClass!.supers) {
Member? member = memberHierarchy.getInterfaceMember(
supertype.classNode, interfaceMember.name,
setter: isSetter);
if (member != null) {
callback(member, isSetter);
}
}
}
void checkOverride(
Types types,
ClassHierarchyMembers memberHierarchy,
Member declaredMember,
Member interfaceMember,
bool isSetter,
callback(Member interfaceMember, bool isSetter),
{required bool isInterfaceCheck,
required Member? localMember}) {
if (declaredMember == interfaceMember) {
return;
}
Member interfaceMemberOrigin =
interfaceMember.memberSignatureOrigin ?? interfaceMember;
if (declaredMember is Constructor || interfaceMember is Constructor) {
unimplemented(
"Constructor in override check.", declaredMember.fileOffset, fileUri);
}
if (declaredMember is Procedure && interfaceMember is Procedure) {
if (declaredMember.kind == interfaceMember.kind) {
if (declaredMember.kind == ProcedureKind.Method ||
declaredMember.kind == ProcedureKind.Operator) {
bool seenCovariant = checkMethodOverride(types, declaredMember,
interfaceMember, interfaceMemberOrigin, isInterfaceCheck,
localMember: localMember);
if (seenCovariant) {
_handleSeenCovariant(
memberHierarchy, interfaceMember, isSetter, callback);
}
} else if (declaredMember.kind == ProcedureKind.Getter) {
checkGetterOverride(types, declaredMember, interfaceMember,
interfaceMemberOrigin, isInterfaceCheck,
localMember: localMember);
} else if (declaredMember.kind == ProcedureKind.Setter) {
bool seenCovariant = checkSetterOverride(types, declaredMember,
interfaceMember, interfaceMemberOrigin, isInterfaceCheck,
localMember: localMember);
if (seenCovariant) {
_handleSeenCovariant(
memberHierarchy, interfaceMember, isSetter, callback);
}
} else {
// Coverage-ignore-block(suite): Not run.
assert(
false,
"Unexpected procedure kind in override check: "
"${declaredMember.kind}");
}
}
} else {
bool declaredMemberHasGetter = declaredMember is Field ||
declaredMember is Procedure && declaredMember.isGetter;
bool interfaceMemberHasGetter = interfaceMember is Field ||
interfaceMember is Procedure && interfaceMember.isGetter;
bool declaredMemberHasSetter = (declaredMember is Field &&
!declaredMember.isFinal &&
!declaredMember.isConst) ||
declaredMember is Procedure && declaredMember.isSetter;
bool interfaceMemberHasSetter = (interfaceMember is Field &&
!(interfaceMember.isFinal && !interfaceMember.isLate) &&
!interfaceMember.isConst) ||
interfaceMember is Procedure && interfaceMember.isSetter;
if (declaredMemberHasGetter && interfaceMemberHasGetter) {
checkGetterOverride(types, declaredMember, interfaceMember,
interfaceMemberOrigin, isInterfaceCheck,
localMember: localMember);
}
if (declaredMemberHasSetter && interfaceMemberHasSetter) {
bool seenCovariant = checkSetterOverride(types, declaredMember,
interfaceMember, interfaceMemberOrigin, isInterfaceCheck,
localMember: localMember);
if (seenCovariant) {
_handleSeenCovariant(
memberHierarchy, interfaceMember, isSetter, callback);
}
}
}
// TODO(ahe): Handle other cases: accessors, operators, and fields.
}
Uri _getMemberUri(Member member) {
if (member is Field) return member.fileUri;
if (member is Procedure) return member.fileUri;
// Other member types won't be seen because constructors don't participate
// in override relationships
return unhandled('${member.runtimeType}', '_getMemberUri', -1, null);
}
Substitution? _computeInterfaceSubstitution(
Types types,
Member declaredMember,
Member interfaceMember,
Member interfaceMemberOrigin,
FunctionNode? declaredFunction,
FunctionNode? interfaceFunction,
bool isInterfaceCheck,
{required Member? localMember}) {
Substitution? interfaceSubstitution;
if (interfaceMember.enclosingClass!.typeParameters.isNotEmpty) {
Class enclosingClass = interfaceMember.enclosingClass!;
interfaceSubstitution = Substitution.fromPairs(
enclosingClass.typeParameters,
types.hierarchy.getInterfaceTypeArgumentsAsInstanceOfClass(
thisType, enclosingClass)!);
}
if (declaredFunction?.typeParameters.length !=
interfaceFunction?.typeParameters.length) {
reportInvalidOverride(
isInterfaceCheck,
declaredMember,
templateOverrideTypeParametersMismatch.withArguments(
"${declaredMember.enclosingClass!.name}."
"${declaredMember.name.text}",
"${interfaceMemberOrigin.enclosingClass!.name}."
"${interfaceMemberOrigin.name.text}"),
declaredMember.fileOffset,
noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMemberOrigin.name.text)
.withLocation(_getMemberUri(interfaceMemberOrigin),
interfaceMemberOrigin.fileOffset, noLength)
],
localMember: localMember);
} else if (declaredFunction?.typeParameters != null) {
// Since the bound of `interfaceFunction!.parameter[i]` may have changed
// during substitution, it can affect the nullabilities of the types in
// the substitution map. The first parameter to
// [TypeParameterType.forAlphaRenaming] should be updated to account for
// the change.
List<TypeParameter> interfaceTypeParameters;
if (interfaceSubstitution == null) {
interfaceTypeParameters = interfaceFunction!.typeParameters;
} else {
FreshTypeParameters freshTypeParameters =
getFreshTypeParameters(interfaceFunction!.typeParameters);
interfaceTypeParameters = freshTypeParameters.freshTypeParameters;
for (TypeParameter parameter in interfaceTypeParameters) {
parameter.bound =
interfaceSubstitution.substituteType(parameter.bound);
}
updateBoundNullabilities(interfaceTypeParameters);
}
Substitution substitution;
if (declaredFunction!.typeParameters.isEmpty) {
substitution = Substitution.empty;
} else if (declaredFunction.typeParameters.length == 1) {
substitution = Substitution.fromSingleton(
interfaceFunction.typeParameters[0],
new TypeParameterType.withDefaultNullability(
declaredFunction.typeParameters[0]));
} else {
Map<TypeParameter, DartType> substitutionMap =
<TypeParameter, DartType>{};
for (int i = 0; i < declaredFunction.typeParameters.length; ++i) {
substitutionMap[interfaceFunction.typeParameters[i]] =
new TypeParameterType.withDefaultNullability(
declaredFunction.typeParameters[i]);
}
substitution = Substitution.fromMap(substitutionMap);
}
for (int i = 0; i < declaredFunction.typeParameters.length; ++i) {
TypeParameter declaredParameter = declaredFunction.typeParameters[i];
TypeParameter interfaceParameter = interfaceFunction.typeParameters[i];
if (!interfaceParameter.isCovariantByClass) {
DartType declaredBound = declaredParameter.bound;
DartType interfaceBound = interfaceParameter.bound;
if (interfaceSubstitution != null) {
declaredBound = interfaceSubstitution.substituteType(declaredBound);
interfaceBound =
interfaceSubstitution.substituteType(interfaceBound);
}
DartType computedBound = substitution.substituteType(interfaceBound);
if (!types
.performNullabilityAwareMutualSubtypesCheck(
declaredBound, computedBound)
.isSubtypeWhenUsingNullabilities()) {
reportInvalidOverride(
isInterfaceCheck,
declaredMember,
templateOverrideTypeParametersBoundMismatch.withArguments(
declaredBound,
declaredParameter.name!,
"${declaredMember.enclosingClass!.name}."
"${declaredMember.name.text}",
computedBound,
"${interfaceMemberOrigin.enclosingClass!.name}."
"${interfaceMemberOrigin.name.text}"),
declaredMember.fileOffset,
noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMemberOrigin.name.text)
.withLocation(_getMemberUri(interfaceMemberOrigin),
interfaceMemberOrigin.fileOffset, noLength)
],
localMember: localMember);
}
}
}
if (interfaceSubstitution != null) {
interfaceSubstitution =
Substitution.combine(interfaceSubstitution, substitution);
} else {
interfaceSubstitution = substitution;
}
}
return interfaceSubstitution;
}
Substitution? _computeDeclaredSubstitution(
Types types, Member declaredMember) {
Substitution? declaredSubstitution;
if (declaredMember.enclosingClass!.typeParameters.isNotEmpty) {
Class enclosingClass = declaredMember.enclosingClass!;
declaredSubstitution = Substitution.fromPairs(
enclosingClass.typeParameters,
types.hierarchy.getInterfaceTypeArgumentsAsInstanceOfClass(
thisType, enclosingClass)!);
}
return declaredSubstitution;
}
void _checkTypes(
Types types,
Substitution? interfaceSubstitution,
Substitution? declaredSubstitution,
Member declaredMember,
Member interfaceMember,
Member interfaceMemberOrigin,
DartType declaredType,
DartType interfaceType,
bool isCovariantByDeclaration,
VariableDeclaration? declaredParameter,
bool isInterfaceCheck,
{bool asIfDeclaredParameter = false,
required Member? localMember}) {
if (interfaceSubstitution != null) {
interfaceType = interfaceSubstitution.substituteType(interfaceType);
}
if (declaredSubstitution != null) {
declaredType = declaredSubstitution.substituteType(declaredType);
}
bool inParameter = declaredParameter != null || asIfDeclaredParameter;
DartType subtype = inParameter ? interfaceType : declaredType;
DartType supertype = inParameter ? declaredType : interfaceType;
if (types.isSubtypeOf(
subtype, supertype, SubtypeCheckMode.withNullabilities)) {
// No problem--the proper subtyping relation is satisfied.
} else if (isCovariantByDeclaration &&
types.isSubtypeOf(
supertype, subtype, SubtypeCheckMode.withNullabilities)) {
// No problem--the overriding parameter is marked "covariant" and has
// a type which is a subtype of the parameter it overrides.
} else if (subtype is InvalidType || supertype is InvalidType) {
// Don't report a problem as something else is wrong that has already
// been reported.
} else {
// Report an error.
String declaredMemberName = '${declaredMember.enclosingClass!.name}'
'.${declaredMember.name.text}';
String interfaceMemberName =
'${interfaceMemberOrigin.enclosingClass!.name}'
'.${interfaceMemberOrigin.name.text}';
Message message;
int fileOffset;
if (declaredParameter == null) {
if (asIfDeclaredParameter) {
// Setter overridden by field
message = templateOverrideTypeMismatchSetter.withArguments(
declaredMemberName,
declaredType,
interfaceType,
interfaceMemberName);
} else {
message = templateOverrideTypeMismatchReturnType.withArguments(
declaredMemberName,
declaredType,
interfaceType,
interfaceMemberName);
}
fileOffset = declaredMember.fileOffset;
} else {
message = templateOverrideTypeMismatchParameter.withArguments(
declaredParameter.name!,
declaredMemberName,
declaredType,
interfaceType,
interfaceMemberName);
fileOffset = declaredParameter.fileOffset;
}
reportInvalidOverride(
isInterfaceCheck, declaredMember, message, fileOffset, noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMemberOrigin.name.text)
.withLocation(_getMemberUri(interfaceMemberOrigin),
interfaceMemberOrigin.fileOffset, noLength)
],
localMember: localMember);
}
}
/// Checks whether [declaredMember] correctly overrides [interfaceMember].
///
/// If an error is reporter [interfaceMemberOrigin] is used as the context
/// for where [interfaceMember] was declared, since [interfaceMember] might
/// itself be synthesized.
///
/// Returns whether a covariant parameter was seen and more methods thus have
/// to be checked.
bool checkMethodOverride(
Types types,
Procedure declaredMember,
Procedure interfaceMember,
Member interfaceMemberOrigin,
bool isInterfaceCheck,
{required Member? localMember}) {
assert(declaredMember.kind == interfaceMember.kind);
assert(declaredMember.kind == ProcedureKind.Method ||
declaredMember.kind == ProcedureKind.Operator);
bool seenCovariant = false;
FunctionNode declaredFunction = declaredMember.function;
FunctionType? declaredSignatureType = declaredMember.signatureType;
FunctionNode interfaceFunction = interfaceMember.function;
FunctionType? interfaceSignatureType = interfaceMember.signatureType;
Substitution? interfaceSubstitution = _computeInterfaceSubstitution(
types,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
declaredFunction,
interfaceFunction,
isInterfaceCheck,
localMember: localMember);
Substitution? declaredSubstitution =
_computeDeclaredSubstitution(types, declaredMember);
_checkTypes(
types,
interfaceSubstitution,
declaredSubstitution,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
declaredFunction.returnType,
interfaceFunction.returnType,
/* isCovariantByDeclaration = */ false,
/* declaredParameter = */ null,
isInterfaceCheck,
localMember: localMember);
if (declaredFunction.positionalParameters.length <
interfaceFunction.positionalParameters.length) {
reportInvalidOverride(
isInterfaceCheck,
declaredMember,
templateOverrideFewerPositionalArguments.withArguments(
"${declaredMember.enclosingClass!.name}."
"${declaredMember.name.text}",
"${interfaceMemberOrigin.enclosingClass!.name}."
"${interfaceMemberOrigin.name.text}"),
declaredMember.fileOffset,
noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMemberOrigin.name.text)
.withLocation(interfaceMemberOrigin.fileUri,
interfaceMemberOrigin.fileOffset, noLength)
],
localMember: localMember);
}
if (interfaceFunction.requiredParameterCount <
declaredFunction.requiredParameterCount) {
reportInvalidOverride(
isInterfaceCheck,
declaredMember,
templateOverrideMoreRequiredArguments.withArguments(
"${declaredMember.enclosingClass!.name}."
"${declaredMember.name.text}",
"${interfaceMemberOrigin.enclosingClass!.name}."
"${interfaceMemberOrigin.name.text}"),
declaredMember.fileOffset,
noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMemberOrigin.name.text)
.withLocation(interfaceMemberOrigin.fileUri,
interfaceMemberOrigin.fileOffset, noLength)
],
localMember: localMember);
}
for (int i = 0;
i < declaredFunction.positionalParameters.length &&
i < interfaceFunction.positionalParameters.length;
i++) {
VariableDeclaration declaredParameter =
declaredFunction.positionalParameters[i];
VariableDeclaration interfaceParameter =
interfaceFunction.positionalParameters[i];
DartType declaredParameterType = declaredParameter.type;
if (declaredSignatureType != null) {
declaredParameterType = declaredSignatureType.positionalParameters[i];
}
DartType interfaceParameterType = interfaceParameter.type;
if (interfaceSignatureType != null) {
interfaceParameterType = interfaceSignatureType.positionalParameters[i];
}
if (i == 0 &&
declaredMember.name == equalsName &&
declaredParameterType ==
types.hierarchy.coreTypes.objectNonNullableRawType &&
interfaceParameter.type is DynamicType) {
// TODO(johnniwinther): Add check for opt-in overrides of operator ==.
// `operator ==` methods in opt-out classes have type
// `bool Function(dynamic)`.
continue;
}
_checkTypes(
types,
interfaceSubstitution,
declaredSubstitution,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
declaredParameterType,
interfaceParameterType,
declaredParameter.isCovariantByDeclaration ||
interfaceParameter.isCovariantByDeclaration,
declaredParameter,
isInterfaceCheck,
localMember: localMember);
if (declaredParameter.isCovariantByDeclaration) seenCovariant = true;
}
if (declaredFunction.namedParameters.isEmpty &&
interfaceFunction.namedParameters.isEmpty) {
return seenCovariant;
}
if (declaredFunction.namedParameters.length <
interfaceFunction.namedParameters.length) {
reportInvalidOverride(
isInterfaceCheck,
declaredMember,
templateOverrideFewerNamedArguments.withArguments(
"${declaredMember.enclosingClass!.name}."
"${declaredMember.name.text}",
"${interfaceMemberOrigin.enclosingClass!.name}."
"${interfaceMemberOrigin.name.text}"),
declaredMember.fileOffset,
noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMemberOrigin.name.text)
.withLocation(interfaceMemberOrigin.fileUri,
interfaceMemberOrigin.fileOffset, noLength)
],
localMember: localMember);
}
int compareNamedParameters(VariableDeclaration p0, VariableDeclaration p1) {
return p0.name!.compareTo(p1.name!);
}
List<VariableDeclaration> sortedFromDeclared =
new List.of(declaredFunction.namedParameters)
..sort(compareNamedParameters);
List<VariableDeclaration> sortedFromInterface =
new List.of(interfaceFunction.namedParameters)
..sort(compareNamedParameters);
Iterator<VariableDeclaration> declaredNamedParameters =
sortedFromDeclared.iterator;
Iterator<VariableDeclaration> interfaceNamedParameters =
sortedFromInterface.iterator;
outer:
while (declaredNamedParameters.moveNext() &&
interfaceNamedParameters.moveNext()) {
while (declaredNamedParameters.current.name !=
interfaceNamedParameters.current.name) {
if (!declaredNamedParameters.moveNext()) {
reportInvalidOverride(
isInterfaceCheck,
declaredMember,
templateOverrideMismatchNamedParameter.withArguments(
"${declaredMember.enclosingClass!.name}."
"${declaredMember.name.text}",
interfaceNamedParameters.current.name!,
"${interfaceMember.enclosingClass!.name}."
"${interfaceMember.name.text}"),
declaredMember.fileOffset,
noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMember.name.text)
.withLocation(interfaceMember.fileUri,
interfaceMember.fileOffset, noLength)
],
localMember: localMember);
break outer;
}
}
VariableDeclaration declaredParameter = declaredNamedParameters.current;
_checkTypes(
types,
interfaceSubstitution,
declaredSubstitution,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
declaredParameter.type,
interfaceNamedParameters.current.type,
declaredParameter.isCovariantByDeclaration,
declaredParameter,
isInterfaceCheck,
localMember: localMember);
if (declaredParameter.isRequired &&
!interfaceNamedParameters.current.isRequired) {
reportInvalidOverride(
isInterfaceCheck,
declaredMember,
templateOverrideMismatchRequiredNamedParameter.withArguments(
declaredParameter.name!,
"${declaredMember.enclosingClass!.name}."
"${declaredMember.name.text}",
"${interfaceMember.enclosingClass!.name}."
"${interfaceMember.name.text}"),
declaredParameter.fileOffset,
noLength,
context: [
templateOverriddenMethodCause
.withArguments(interfaceMemberOrigin.name.text)
.withLocation(_getMemberUri(interfaceMemberOrigin),
interfaceMemberOrigin.fileOffset, noLength)
],
localMember: localMember);
}
if (declaredParameter.isCovariantByDeclaration) seenCovariant = true;
}
return seenCovariant;
}
/// Checks whether [declaredMember] correctly overrides [interfaceMember].
///
/// If an error is reporter [interfaceMemberOrigin] is used as the context
/// for where [interfaceMember] was declared, since [interfaceMember] might
/// itself be synthesized.
void checkGetterOverride(
Types types,
Member declaredMember,
Member interfaceMember,
Member interfaceMemberOrigin,
bool isInterfaceCheck,
{required Member? localMember}) {
Substitution? interfaceSubstitution = _computeInterfaceSubstitution(
types,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
/* declaredFunction = */
null,
/* interfaceFunction = */
null,
isInterfaceCheck,
localMember: localMember);
Substitution? declaredSubstitution =
_computeDeclaredSubstitution(types, declaredMember);
DartType declaredType = declaredMember.getterType;
DartType interfaceType = interfaceMember.getterType;
_checkTypes(
types,
interfaceSubstitution,
declaredSubstitution,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
declaredType,
interfaceType,
/* isCovariantByDeclaration = */
false,
/* declaredParameter = */
null,
isInterfaceCheck,
localMember: localMember);
}
/// Checks whether [declaredMember] correctly overrides [interfaceMember].
///
/// If an error is reporter [interfaceMemberOrigin] is used as the context
/// for where [interfaceMember] was declared, since [interfaceMember] might
/// itself be synthesized.
///
/// Returns whether a covariant parameter was seen and more methods thus have
/// to be checked.
bool checkSetterOverride(
Types types,
Member declaredMember,
Member interfaceMember,
Member interfaceMemberOrigin,
bool isInterfaceCheck,
{required Member? localMember}) {
Substitution? interfaceSubstitution = _computeInterfaceSubstitution(
types,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
/* declaredFunction = */
null,
/* interfaceFunction = */
null,
isInterfaceCheck,
localMember: localMember);
Substitution? declaredSubstitution =
_computeDeclaredSubstitution(types, declaredMember);
DartType declaredType = declaredMember.setterType;
DartType interfaceType = interfaceMember.setterType;
VariableDeclaration? declaredParameter =
declaredMember.function?.positionalParameters.elementAt(0);
bool isCovariantByDeclaration =
declaredParameter?.isCovariantByDeclaration ?? false;
if (!isCovariantByDeclaration && declaredMember is Field) {
isCovariantByDeclaration = declaredMember.isCovariantByDeclaration;
}
if (!isCovariantByDeclaration && interfaceMember is Field) {
isCovariantByDeclaration = interfaceMember.isCovariantByDeclaration;
}
_checkTypes(
types,
interfaceSubstitution,
declaredSubstitution,
declaredMember,
interfaceMember,
interfaceMemberOrigin,
declaredType,
interfaceType,
isCovariantByDeclaration,
declaredParameter,
isInterfaceCheck,
asIfDeclaredParameter: true,
localMember: localMember);
return isCovariantByDeclaration;
}
// When the overriding member is inherited, report the class containing
// the conflict as the main error.
void reportInvalidOverride(bool isInterfaceCheck, Member declaredMember,
Message message, int fileOffset, int length,
{List<LocatedMessage>? context, required Member? localMember}) {
if (shouldOverrideProblemBeOverlooked(this)) {
return;
}
switch (localMember) {
case Procedure():
localMember.isErroneous = true;
case Field():
localMember.isErroneous = true;
case Constructor():
// Coverage-ignore(suite): Not run.
unexpected("Procedure|Field", "Constructor", fileOffset, fileUri);
case null:
// Do nothing.
}
if (declaredMember.enclosingClass == cls) {
// Ordinary override
libraryBuilder.addProblem(
message, fileOffset, length, declaredMember.fileUri,
context: context);
} else {
context = [
message.withLocation(declaredMember.fileUri, fileOffset, length),
...?context
];
if (isInterfaceCheck) {
// Interface check
libraryBuilder.addProblem(
templateInterfaceCheck.withArguments(
declaredMember.name.text, cls.name),
cls.fileOffset,
cls.name.length,
cls.fileUri,
context: context);
} else {
if (cls.isAnonymousMixin) {
// Implicit mixin application class
String baseName = cls.superclass!.demangledName;
String mixinName = cls.mixedInClass!.name;
int classNameLength = cls.nameAsMixinApplicationSubclass.length;
libraryBuilder.addProblem(
templateImplicitMixinOverride.withArguments(
mixinName, baseName, declaredMember.name.text),
cls.fileOffset,
classNameLength,
cls.fileUri,
context: context);
} else {
// Named mixin application class
libraryBuilder.addProblem(
templateNamedMixinOverride.withArguments(
cls.name, declaredMember.name.text),
cls.fileOffset,
cls.name.length,
cls.fileUri,
context: context);
}
}
}
}
// Coverage-ignore(suite): Not run.
/// Returns an iterator the origin class and all augmentations in application
/// order.
Iterator<SourceClassBuilder> get declarationIterator =>
new AugmentationIterator<SourceClassBuilder>(this, null);
@override
// Coverage-ignore(suite): Not run.
Reference get reference => cls.reference;
}
/// Returns `true` if override problems should be overlooked.
///
/// This is needed for the current encoding of some JavaScript implementation
/// classes that are not valid Dart. For instance `JSInt` in
/// 'dart:_interceptors' that implements both `int` and `double`, and `JsArray`
/// in `dart:js` that implement both `ListMixin` and `JsObject`.
bool shouldOverrideProblemBeOverlooked(ClassBuilder classBuilder) {
return getOverlookedOverrideProblemChoice(classBuilder) != null;
}
/// Returns the index of the member to use if an override problems should be
/// overlooked.
///
/// This is needed for the current encoding of some JavaScript implementation
/// classes that are not valid Dart. For instance `JSInt` in
/// 'dart:_interceptors' that implements both `int` and `double`, and `JsArray`
/// in `dart:js` that implement both `ListMixin` and `JsObject`.
int? getOverlookedOverrideProblemChoice(DeclarationBuilder declarationBuilder) {
String uri = '${declarationBuilder.libraryBuilder.importUri}';
if (uri == 'dart:js' &&
// Coverage-ignore(suite): Not run.
declarationBuilder.fileUri.pathSegments.last == 'js.dart') {
return 0;
} else if (uri == 'dart:_interceptors' &&
// Coverage-ignore(suite): Not run.
declarationBuilder.fileUri.pathSegments.last == 'js_number.dart') {
return 1;
}
return null;
}
TypeBuilder? _applyMixins(
{required ProblemReporting problemReporting,
required SourceLibraryBuilder enclosingLibraryBuilder,
required List<NominalParameterBuilder> unboundNominalParameters,
required TypeBuilder? supertype,
required List<TypeBuilder>? mixins,
required int startOffset,
required int nameOffset,
required int endOffset,
required String subclassName,
required bool isMixinDeclaration,
required IndexedLibrary? indexedLibrary,
required LookupScope compilationUnitScope,
required Map<SourceClassBuilder, TypeBuilder> mixinApplications,
required Uri fileUri,
List<SourceNominalParameterBuilder>? typeParameters,
required Modifiers modifiers,
required TypeBuilder objectTypeBuilder,
required void Function(SourceClassBuilder) onAnonymousMixin}) {
if (mixins == null) {
return supertype;
}
// Documentation below assumes the given mixin application is in one of
// these forms:
//
// class C extends S with M1, M2, M3;
// class Named = S with M1, M2, M3;
//
// When we refer to the subclass, we mean `C` or `Named`.
/// The current supertype.
///
/// Starts out having the value `S` and on each iteration of the loop
/// below, it will take on the value corresponding to:
///
/// 1. `S with M1`.
/// 2. `(S with M1) with M2`.
/// 3. `((S with M1) with M2) with M3`.
supertype ??= objectTypeBuilder;
/// The variable part of the mixin application's synthetic name. It
/// starts out as the name of the superclass, but is only used after it
/// has been combined with the name of the current mixin. In the examples
/// from above, it will take these values:
///
/// 1. `S&M1`
/// 2. `S&M1&M2`
/// 3. `S&M1&M2&M3`.
///
/// The full name of the mixin application is obtained by prepending the
/// name of the subclass (`C` or `Named` in the above examples) to the
/// running name. For the example `C`, that leads to these full names:
///
/// 1. `_C&S&M1`
/// 2. `_C&S&M1&M2`
/// 3. `_C&S&M1&M2&M3`.
///
/// For a named mixin application, the last name has been given by the
/// programmer, so for the example `Named` we see these full names:
///
/// 1. `_Named&S&M1`
/// 2. `_Named&S&M1&M2`
/// 3. `Named`.
String runningName;
if (supertype.typeName == null) {
assert(supertype is FunctionTypeBuilder);
// Function types don't have names, and we can supply any string that
// doesn't have to be unique. The actual supertype of the mixin will
// not be built in that case.
runningName = "";
} else {
runningName = supertype.typeName!.name;
}
/// The names of the type parameters of the subclass.
Set<String>? typeParameterNames;
if (typeParameters != null) {
typeParameterNames = new Set<String>();
for (NominalParameterBuilder typeParameter in typeParameters) {
typeParameterNames.add(typeParameter.name);
}
}
/// Iterate over the mixins from left to right. At the end of each
/// iteration, a new [supertype] is computed that is the mixin
/// application of [supertype] with the current mixin.
for (int i = 0; i < mixins.length; i++) {
TypeBuilder mixin = mixins[i];
bool isGeneric = false;
if (typeParameterNames != null) {
if (supertype != null) {
isGeneric =
isGeneric || supertype.usesTypeParameters(typeParameterNames);
}
isGeneric = isGeneric || mixin.usesTypeParameters(typeParameterNames);
}
TypeName? typeName = mixin.typeName;
if (typeName != null) {
runningName += "&${typeName.name}";
}
String fullname = "_$subclassName&$runningName";
List<SourceNominalParameterBuilder>? applicationTypeParameters;
List<TypeBuilder>? applicationTypeArguments;
ClassDeclaration classDeclaration;
final int computedStartOffset;
// Otherwise, we pass the fresh type parameters to the mixin
// application in the same order as they're declared on the subclass.
if (isGeneric) {
NominalParameterNameSpace nominalParameterNameSpace =
new NominalParameterNameSpace();
NominalParameterCopy nominalVariableCopy =
NominalParameterCopy.copyTypeParameters(
unboundNominalParameters: unboundNominalParameters,
oldParameterBuilders: typeParameters,
kind: TypeParameterKind.extensionSynthesized,
instanceTypeParameterAccess:
InstanceTypeParameterAccessState.Allowed)!;
applicationTypeParameters = nominalVariableCopy.newParameterBuilders;
Map<NominalParameterBuilder, NominalParameterBuilder>
newToOldVariableMap = nominalVariableCopy.newToOldParameterMap;
Map<NominalParameterBuilder, TypeBuilder> substitutionMap =
nominalVariableCopy.substitutionMap;
applicationTypeArguments = [];
for (NominalParameterBuilder typeParameter in typeParameters!) {
TypeBuilder applicationTypeArgument =
new NamedTypeBuilderImpl.fromTypeDeclarationBuilder(
// The type parameter types passed as arguments to the
// generic class representing the anonymous mixin
// application should refer back to the type parameters of
// the class that extend the anonymous mixin application.
typeParameter,
const NullabilityBuilder.omitted(),
fileUri: fileUri,
charOffset: nameOffset,
instanceTypeParameterAccess:
InstanceTypeParameterAccessState.Allowed);
applicationTypeArguments.add(applicationTypeArgument);
}
nominalParameterNameSpace.addTypeParameters(
problemReporting, applicationTypeParameters,
ownerName: fullname, allowNameConflict: true);
if (supertype != null) {
supertype = new SynthesizedTypeBuilder(
supertype, newToOldVariableMap, substitutionMap);
}
mixin = new SynthesizedTypeBuilder(
mixin, newToOldVariableMap, substitutionMap);
}
computedStartOffset = startOffset;
classDeclaration = new AnonymousMixinApplication(
name: fullname,
compilationUnitScope: compilationUnitScope,
supertype: isMixinDeclaration ? null : supertype,
interfaces: isMixinDeclaration ? [supertype!, mixin] : null,
fileUri: fileUri,
startOffset: computedStartOffset,
nameOffset: nameOffset,
endOffset: endOffset);
IndexedClass? indexedClass;
if (indexedLibrary != null) {
indexedClass = indexedLibrary.lookupIndexedClass(fullname);
}
LookupScope typeParameterScope =
TypeParameterScope.fromList(compilationUnitScope, typeParameters);
DeclarationNameSpaceBuilder nameSpaceBuilder =
new DeclarationNameSpaceBuilder.empty();
SourceClassBuilder application = new SourceClassBuilder(
modifiers: Modifiers.Abstract,
name: fullname,
typeParameters: applicationTypeParameters,
typeParameterScope: typeParameterScope,
nameSpaceBuilder: nameSpaceBuilder,
libraryBuilder: enclosingLibraryBuilder,
fileUri: fileUri,
nameOffset: nameOffset,
indexedClass: indexedClass,
mixedInTypeBuilder: isMixinDeclaration ? null : mixin,
introductory: classDeclaration);
// TODO(ahe, kmillikin): Should always be true?
// pkg/analyzer/test/src/summary/resynthesize_kernel_test.dart can't
// handle that :(
application.cls.isAnonymousMixin = true;
onAnonymousMixin(application);
supertype = new NamedTypeBuilderImpl.fromTypeDeclarationBuilder(
application, const NullabilityBuilder.omitted(),
arguments: applicationTypeArguments,
fileUri: fileUri,
charOffset: nameOffset,
instanceTypeParameterAccess: InstanceTypeParameterAccessState.Allowed);
mixinApplications[application] = mixin;
}
return supertype;
}