blob: d9654829a599fc629114910df546560d41e3da0a [file] [log] [blame]
// Copyright (c) 2022, 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/problems.dart';
import '../base/scope.dart';
import '../builder/builder.dart';
import '../builder/constructor_reference_builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/function_builder.dart';
import '../builder/member_builder.dart';
import '../builder/name_iterator.dart';
import '../builder/type_builder.dart';
import '../codes/cfe_codes.dart';
import '../dill/dill_member_builder.dart';
import '../type_inference/type_schema.dart';
import 'source_factory_builder.dart';
import 'source_library_builder.dart';
/// Common interface for builders for a class declarations in source code, such
/// as a regular class declaration and an extension type declaration.
// TODO(johnniwinther): Should this be renamed now that inline classes are
// renamed to extension type declarations?
abstract class ClassDeclaration
implements IDeclarationBuilder, ClassMemberAccess {
@override
SourceLibraryBuilder get libraryBuilder;
bool get isMixinDeclaration;
int resolveConstructors(SourceLibraryBuilder library);
/// [Iterator] for all members declared directly in this class, including
/// augmenting members.
///
/// Duplicates are _not_ included.
///
/// For instance:
///
/// class Class {
/// // Declared, so it is included for this class but not for the
/// // augmentation class below.
/// method() {}
/// // Declared, so it is included for this class but not for the
/// // augmentation class below.
/// method2() {}
/// method2() {} // Duplicate, so it is *not* included.
/// }
///
/// augment class Class {
/// // Augmenting, so it is included for this augmentation class but
/// // not for the origin class above.
/// augment method() {}
/// // Declared, so it is included for this augmentation class but not
/// // for the origin class above.
/// extra() {}
/// }
///
Iterator<T> localMemberIterator<T extends Builder>();
/// [Iterator] for all constructors declared directly in this class, including
/// augmenting constructors.
///
/// For instance:
///
/// class Class {
/// // Declared, so it is included for this class but not for the
/// // augmentation class below.
/// Class();
/// // Declared, so it is included for this class but not for the
/// // augmentation class below.
/// Class.named();
/// Class.named(); // Duplicate, so it is *not* included.
/// }
///
/// augment class Class {
/// // Augmenting, so it is included for this augmentation class but
/// // not for the origin class above.
/// augment Class();
/// // Declared, so it is included for this augmentation class but not
/// // for the origin class above.
/// Class.extra();
/// }
///
Iterator<T> localConstructorIterator<T extends MemberBuilder>();
}
mixin ClassDeclarationMixin implements ClassDeclaration {
List<ConstructorReferenceBuilder>? get constructorReferences;
@override
int resolveConstructors(SourceLibraryBuilder library) {
if (constructorReferences == null) return 0;
for (ConstructorReferenceBuilder ref in constructorReferences!) {
ref.resolveIn(scope, library);
}
int count = constructorReferences!.length;
if (count != 0) {
Iterator<MemberBuilder> iterator = nameSpace.filteredConstructorIterator(
parent: this, includeDuplicates: true, includeAugmentations: true);
while (iterator.moveNext()) {
MemberBuilder declaration = iterator.current;
if (declaration.declarationBuilder?.origin != origin) {
unexpected("$fileUri", "${declaration.declarationBuilder!.fileUri}",
charOffset, fileUri);
}
if (declaration is RedirectingFactoryBuilder) {
// Compute the immediate redirection target, not the effective.
ConstructorReferenceBuilder redirectionTarget =
declaration.redirectionTarget;
List<TypeBuilder>? typeArguments = redirectionTarget.typeArguments;
Builder? target = redirectionTarget.target;
if (typeArguments != null && target is MemberBuilder) {
TypeName redirectionTargetName = redirectionTarget.typeName;
if (redirectionTargetName.qualifier == null) {
// Do nothing. This is the case of an identifier followed by
// type arguments, such as the following:
// B<T>
// B<T>.named
} else {
if (target.name.isEmpty) {
// Do nothing. This is the case of a qualified
// non-constructor prefix (for example, with a library
// qualifier) followed by type arguments, such as the
// following:
// lib.B<T>
} else if (target.name != redirectionTargetName.name) {
// Do nothing. This is the case of a qualified
// non-constructor prefix followed by type arguments followed
// by a constructor name, such as the following:
// lib.B<T>.named
} else {
// TODO(cstefantsova,johnniwinther): Handle this in case in
// ConstructorReferenceBuilder.resolveIn and unify with other
// cases of handling of type arguments after constructor
// names.
addProblem(
messageConstructorWithTypeArguments,
redirectionTargetName.nameOffset,
redirectionTargetName.nameLength);
}
}
}
Builder? targetBuilder = redirectionTarget.target;
Member? targetNode;
if (targetBuilder is FunctionBuilder) {
targetNode = targetBuilder.member;
} else if (targetBuilder is DillMemberBuilder) {
targetNode = targetBuilder.member;
} else if (targetBuilder is AmbiguousBuilder) {
libraryBuilder.addProblemForRedirectingFactory(
declaration,
templateDuplicatedDeclarationUse
.withArguments(redirectionTarget.fullNameForErrors),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
} else {
libraryBuilder.addProblemForRedirectingFactory(
declaration,
templateRedirectionTargetNotFound
.withArguments(redirectionTarget.fullNameForErrors),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
}
if (targetNode != null &&
targetNode is Constructor &&
targetNode.enclosingClass.isAbstract) {
libraryBuilder.addProblemForRedirectingFactory(
declaration,
templateAbstractRedirectedClassInstantiation
.withArguments(redirectionTarget.fullNameForErrors),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
targetNode = null;
}
if (targetNode != null &&
targetNode is Constructor &&
targetNode.enclosingClass.isEnum) {
libraryBuilder.addProblemForRedirectingFactory(
declaration,
messageEnumFactoryRedirectsToConstructor,
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
targetNode = null;
}
if (targetNode != null) {
List<DartType>? typeArguments = declaration.typeArguments;
if (typeArguments == null) {
int typeArgumentCount;
if (targetBuilder!.isExtensionTypeMember) {
ExtensionTypeDeclarationBuilder
extensionTypeDeclarationBuilder =
targetBuilder.parent as ExtensionTypeDeclarationBuilder;
typeArgumentCount =
extensionTypeDeclarationBuilder.typeVariablesCount;
} else {
typeArgumentCount =
targetNode.enclosingClass!.typeParameters.length;
}
typeArguments = new List<DartType>.filled(
typeArgumentCount, const UnknownType());
}
declaration.setRedirectingFactoryBody(targetNode, typeArguments);
}
}
}
}
return count;
}
}
abstract class ClassDeclarationAugmentationAccess<D extends ClassDeclaration> {
D getOrigin(D classDeclaration);
Iterable<D>? getAugmentations(D classDeclaration);
}
class ClassDeclarationMemberIterator<D extends ClassDeclaration,
T extends Builder> implements Iterator<T> {
Iterator<T>? _iterator;
Iterator<D>? augmentationBuilders;
final bool includeDuplicates;
factory ClassDeclarationMemberIterator.full(
ClassDeclarationAugmentationAccess<D> access, D classBuilder,
{required bool includeDuplicates}) {
return new ClassDeclarationMemberIterator._(access.getOrigin(classBuilder),
access.getAugmentations(classBuilder)?.iterator,
includeDuplicates: includeDuplicates);
}
// Coverage-ignore(suite): Not run.
factory ClassDeclarationMemberIterator.local(D classBuilder,
{required bool includeDuplicates}) {
return new ClassDeclarationMemberIterator._(classBuilder, null,
includeDuplicates: includeDuplicates);
}
ClassDeclarationMemberIterator._(
D classDeclaration, this.augmentationBuilders,
{required this.includeDuplicates})
: _iterator = classDeclaration.nameSpace.filteredIterator<T>(
parent: classDeclaration,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
@override
bool moveNext() {
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
if (augmentationBuilders != null && augmentationBuilders!.moveNext()) {
D augmentationClassDeclaration = augmentationBuilders!.current;
_iterator = augmentationClassDeclaration.nameSpace.filteredIterator<T>(
parent: augmentationClassDeclaration,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
}
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
return false;
}
@override
T get current =>
_iterator?.current ?? // Coverage-ignore(suite): Not run.
(throw new StateError('No element'));
}
// Coverage-ignore(suite): Not run.
class ClassDeclarationMemberNameIterator<D extends ClassDeclaration,
T extends Builder> implements NameIterator<T> {
NameIterator<T>? _iterator;
Iterator<D>? augmentationBuilders;
final bool includeDuplicates;
factory ClassDeclarationMemberNameIterator(
ClassDeclarationAugmentationAccess<D> access, D classBuilder,
{required bool includeDuplicates}) {
return new ClassDeclarationMemberNameIterator._(
access.getOrigin(classBuilder),
access.getAugmentations(classBuilder)?.iterator,
includeDuplicates: includeDuplicates);
}
ClassDeclarationMemberNameIterator._(
D classDeclaration, this.augmentationBuilders,
{required this.includeDuplicates})
: _iterator = classDeclaration.nameSpace.filteredNameIterator<T>(
parent: classDeclaration,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
@override
bool moveNext() {
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
if (augmentationBuilders != null && augmentationBuilders!.moveNext()) {
D augmentationClassDeclaration = augmentationBuilders!.current;
_iterator = augmentationClassDeclaration.nameSpace
.filteredNameIterator<T>(
parent: augmentationClassDeclaration,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
}
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
return false;
}
@override
T get current => _iterator?.current ?? (throw new StateError('No element'));
@override
String get name => _iterator?.name ?? (throw new StateError('No element'));
}
class ClassDeclarationConstructorIterator<D extends ClassDeclaration,
T extends MemberBuilder> implements Iterator<T> {
Iterator<T>? _iterator;
Iterator<D>? augmentationBuilders;
final bool includeDuplicates;
factory ClassDeclarationConstructorIterator.full(
ClassDeclarationAugmentationAccess<D> access, D classBuilder,
{required bool includeDuplicates}) {
return new ClassDeclarationConstructorIterator._(
access.getOrigin(classBuilder),
access.getAugmentations(classBuilder)?.iterator,
includeDuplicates: includeDuplicates);
}
// Coverage-ignore(suite): Not run.
factory ClassDeclarationConstructorIterator.local(D classBuilder,
{required bool includeDuplicates}) {
return new ClassDeclarationConstructorIterator._(classBuilder, null,
includeDuplicates: includeDuplicates);
}
ClassDeclarationConstructorIterator._(
D classDeclaration, this.augmentationBuilders,
{required this.includeDuplicates})
: _iterator = classDeclaration.nameSpace.filteredConstructorIterator<T>(
parent: classDeclaration,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
@override
bool moveNext() {
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
if (augmentationBuilders != null && augmentationBuilders!.moveNext()) {
D augmentationClassDeclaration = augmentationBuilders!.current;
_iterator = augmentationClassDeclaration.nameSpace
.filteredConstructorIterator<T>(
parent: augmentationClassDeclaration,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
}
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
return false;
}
@override
T get current =>
_iterator?.current ?? // Coverage-ignore(suite): Not run.
(throw new StateError('No element'));
}
class ClassDeclarationConstructorNameIterator<D extends ClassDeclaration,
T extends MemberBuilder> implements NameIterator<T> {
NameIterator<T>? _iterator;
Iterator<D>? augmentationBuilders;
final bool includeDuplicates;
factory ClassDeclarationConstructorNameIterator(
ClassDeclarationAugmentationAccess<D> access, D classDeclaration,
{required bool includeDuplicates}) {
return new ClassDeclarationConstructorNameIterator._(
access.getOrigin(classDeclaration),
access.getAugmentations(classDeclaration)?.iterator,
includeDuplicates: includeDuplicates);
}
ClassDeclarationConstructorNameIterator._(
D classBuilder, this.augmentationBuilders,
{required this.includeDuplicates})
: _iterator = classBuilder.nameSpace.filteredConstructorNameIterator<T>(
parent: classBuilder,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
@override
bool moveNext() {
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
if (augmentationBuilders != null && augmentationBuilders!.moveNext()) {
D augmentationClassDeclaration = augmentationBuilders!.current;
_iterator = augmentationClassDeclaration.nameSpace
.filteredConstructorNameIterator<T>(
parent: augmentationClassDeclaration,
includeDuplicates: includeDuplicates,
includeAugmentations: false);
}
if (_iterator != null) {
if (_iterator!.moveNext()) {
return true;
}
}
return false;
}
@override
T get current =>
_iterator?.current ?? // Coverage-ignore(suite): Not run.
(throw new StateError('No element'));
@override
String get name =>
_iterator?.name ?? // Coverage-ignore(suite): Not run.
(throw new StateError('No element'));
}