| // 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'; |
| import 'package:kernel/type_environment.dart'; |
| |
| import '../builder/builder.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/name_iterator.dart'; |
| import '../builder/prefix_builder.dart'; |
| import '../kernel/hierarchy/class_member.dart' show ClassMember; |
| import '../kernel/kernel_helper.dart'; |
| import '../kernel/load_library_builder.dart'; |
| import '../kernel/type_algorithms.dart'; |
| import '../source/source_class_builder.dart'; |
| import '../source/source_extension_builder.dart'; |
| import '../source/source_library_builder.dart'; |
| import '../source/source_member_builder.dart'; |
| import 'lookup_result.dart'; |
| import 'messages.dart'; |
| import 'name_space.dart'; |
| import 'uri_offset.dart'; |
| |
| enum ScopeKind { |
| /// Scope of pattern switch-case statements |
| /// |
| /// These scopes receive special treatment in that they are end-points of the |
| /// scope stack in presence of multiple heads for the same case, but can have |
| /// nested scopes if it's just a single head. In that latter possibility the |
| /// body of the case is nested into the scope of the case head. And for switch |
| /// expressions that scope includes both the head and the case expression. |
| caseHead, |
| |
| /// The declaration-level scope for classes, enums, and similar declarations |
| declaration, |
| |
| /// Scope where the formal parameters of a function are declared |
| formals, |
| |
| /// Scope of a `for` statement |
| forStatement, |
| |
| /// Scope of a function body |
| functionBody, |
| |
| /// Scope of the head of the if-case statement |
| ifCaseHead, |
| |
| /// Scope of an if-element in a collection |
| ifElement, |
| |
| /// Scope for the initializers of generative constructors |
| initializers, |
| |
| /// Scope where the joint variables of a switch case are declared |
| jointVariables, |
| |
| /// Scope where labels of labelled statements are declared |
| labels, |
| |
| /// Top-level scope of a library |
| library, |
| |
| /// The special scope of the named function expression |
| /// |
| /// This scope is treated separately because the named function expressions |
| /// are allowed to be recursive, and the name of that function expression |
| /// should be visible in the scope of the function itself. |
| namedFunctionExpression, |
| |
| /// The scope of the RHS of a binary-or pattern |
| /// |
| /// It is utilized for separating the branch-local variables from the joint |
| /// variables of the overall binary-or pattern. |
| orPatternRight, |
| |
| /// The scope of a pattern |
| /// |
| /// It contains the variables associated with pattern variable declarations. |
| pattern, |
| |
| /// Local scope of a statement, such as the body of a while loop |
| statementLocalScope, |
| |
| /// Local scope of a switch block |
| switchBlock, |
| |
| /// Scope for switch cases |
| /// |
| /// This scope kind is used in assertion checks. |
| switchCase, |
| |
| /// Scope for switch case bodies |
| /// |
| /// This is used to handle local variables of switch cases. |
| switchCaseBody, |
| |
| /// Scope for type parameters of declarations |
| typeParameters, |
| |
| /// Scope for a compilation unit. |
| /// |
| /// This contains the entities declared in the library to which the |
| /// compilation unit belongs. Its parent scopes are the [prefix] and [import] |
| /// scopes of the compilation unit. |
| compilationUnit, |
| |
| /// Scope for the prefixed imports in a compilation unit. |
| /// |
| /// The parent scope is the [import] scope of the same compilation unit. |
| prefix, |
| |
| /// Scope for the non-prefixed imports in a compilation unit. |
| /// |
| /// The parent scope is the [prefix] scope of the parent compilation unit, |
| /// if any. |
| import, |
| } |
| |
| abstract class LookupScope { |
| ScopeKind get kind; |
| LookupResult? lookup(String name, int fileOffset, Uri fileUri); |
| // TODO(johnniwinther): Should this be moved to an outer scope interface? |
| void forEachExtension(void Function(ExtensionBuilder) f); |
| } |
| |
| /// A [LookupScope] based directly on a [NameSpace]. |
| abstract class BaseNameSpaceLookupScope implements LookupScope { |
| @override |
| final ScopeKind kind; |
| |
| final String classNameOrDebugName; |
| |
| BaseNameSpaceLookupScope(this.kind, this.classNameOrDebugName); |
| |
| NameSpace get _nameSpace; |
| |
| LookupScope? get _parent; |
| |
| @override |
| LookupResult? lookup(String name, int fileOffset, Uri fileUri) { |
| return _nameSpace.lookupLocal(name, |
| fileUri: fileUri, fileOffset: fileOffset, staticOnly: false) ?? |
| _parent?.lookup(name, fileOffset, fileUri); |
| } |
| |
| @override |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| _nameSpace.forEachLocalExtension(f); |
| _parent?.forEachExtension(f); |
| } |
| |
| @override |
| String toString() => "$runtimeType(${kind},$classNameOrDebugName)"; |
| } |
| |
| class NameSpaceLookupScope extends BaseNameSpaceLookupScope { |
| @override |
| final NameSpace _nameSpace; |
| |
| @override |
| final LookupScope? _parent; |
| |
| NameSpaceLookupScope(this._nameSpace, super.kind, super.classNameOrDebugName, |
| {LookupScope? parent}) |
| : _parent = parent; |
| } |
| |
| abstract class AbstractTypeParameterScope implements LookupScope { |
| final LookupScope _parent; |
| |
| AbstractTypeParameterScope(this._parent); |
| |
| TypeParameterBuilder? getTypeParameter(String name); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| ScopeKind get kind => ScopeKind.typeParameters; |
| |
| @override |
| LookupResult? lookup(String name, int fileOffset, Uri fileUri) { |
| LookupResult? result = getTypeParameter(name); |
| return result ?? _parent.lookup(name, fileOffset, fileUri); |
| } |
| |
| @override |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| _parent.forEachExtension(f); |
| } |
| |
| @override |
| String toString() => "$runtimeType(${kind},type parameter)"; |
| } |
| |
| class TypeParameterScope extends AbstractTypeParameterScope { |
| final Map<String, TypeParameterBuilder> _typeParameters; |
| |
| TypeParameterScope(super._parent, this._typeParameters); |
| |
| @override |
| TypeParameterBuilder? getTypeParameter(String name) => _typeParameters[name]; |
| |
| static LookupScope fromList( |
| LookupScope parent, List<TypeParameterBuilder>? typeParameterBuilders) { |
| if (typeParameterBuilders == null) return parent; |
| Map<String, TypeParameterBuilder> map = {}; |
| for (TypeParameterBuilder typeParameterBuilder in typeParameterBuilders) { |
| if (typeParameterBuilder.isWildcard) continue; |
| map[typeParameterBuilder.name] = typeParameterBuilder; |
| } |
| return new TypeParameterScope(parent, map); |
| } |
| } |
| |
| /// The import scope of a compilation unit. |
| /// |
| /// This includes all declaration available through imports in this compilation |
| /// unit. Its parent scope is the prefix scope of the parent compilation unit. |
| /// If the compilation unit has no parent, the |
| /// [SourceLibraryBuilder.parentScope] is used as the parent. This is not a |
| /// normal Dart scope, but instead a synthesized scope used for expression |
| /// compilation. |
| class CompilationUnitImportScope extends BaseNameSpaceLookupScope { |
| final SourceCompilationUnit _compilationUnit; |
| final NameSpace _importNameSpace; |
| |
| CompilationUnitImportScope(this._compilationUnit, this._importNameSpace) |
| : super(ScopeKind.import, 'import'); |
| |
| @override |
| NameSpace get _nameSpace => _importNameSpace; |
| |
| @override |
| LookupScope? get _parent => |
| _compilationUnit.parentCompilationUnit?.prefixScope ?? |
| _compilationUnit.libraryBuilder.parentScope; |
| } |
| |
| /// The scope of a compilation unit. |
| /// |
| /// This is the enclosing scope for all declarations within the compilation |
| /// unit. It gives access to all declarations in the library the compilation |
| /// unit is part of. Its parent scope is the prefix scope, which contains all |
| /// imports with prefixes declared in this compilation unit. The grand parent |
| /// scope is the import scope of the compilation unit implemented through |
| /// [CompilationUnitImportScope]. |
| class CompilationUnitScope extends BaseNameSpaceLookupScope { |
| final SourceCompilationUnit _compilationUnit; |
| |
| @override |
| final LookupScope? _parent; |
| |
| CompilationUnitScope( |
| this._compilationUnit, super.kind, super.classNameOrDebugName, |
| {LookupScope? parent}) |
| : _parent = parent; |
| |
| @override |
| NameSpace get _nameSpace => _compilationUnit.libraryBuilder.libraryNameSpace; |
| |
| /// Set of extension declarations in scope. This is computed lazily in |
| /// [forEachExtension]. |
| Set<ExtensionBuilder>? _extensions; |
| |
| @override |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| if (_extensions == null) { |
| Set<ExtensionBuilder> extensions = _extensions = <ExtensionBuilder>{}; |
| _parent?.forEachExtension(extensions.add); |
| _nameSpace.forEachLocalExtension(extensions.add); |
| } |
| _extensions!.forEach(f); |
| } |
| } |
| |
| /// The scope containing the prefixes imported into a compilation unit. |
| class CompilationUnitPrefixScope extends BaseNameSpaceLookupScope { |
| @override |
| final NameSpace _nameSpace; |
| |
| @override |
| final LookupScope? _parent; |
| |
| CompilationUnitPrefixScope( |
| this._nameSpace, super.kind, super.classNameOrDebugName, |
| {required LookupScope? parent}) |
| : _parent = parent; |
| |
| /// Set of extension declarations in scope. This is computed lazily in |
| /// [forEachExtension]. |
| Set<ExtensionBuilder>? _extensions; |
| |
| @override |
| void forEachExtension(void Function(ExtensionBuilder) f) { |
| if (_extensions == null) { |
| Set<ExtensionBuilder> extensions = _extensions = {}; |
| Iterator<PrefixBuilder> iterator = |
| _nameSpace.filteredIterator(includeDuplicates: false); |
| while (iterator.moveNext()) { |
| iterator.current.forEachExtension((e) { |
| extensions.add(e); |
| }); |
| } |
| _parent?.forEachExtension(extensions.add); |
| } |
| _extensions!.forEach(f); |
| } |
| } |
| |
| class DeclarationBuilderScope extends BaseNameSpaceLookupScope { |
| DeclarationBuilder? _declarationBuilder; |
| |
| @override |
| final LookupScope? _parent; |
| |
| DeclarationBuilderScope(this._parent) |
| : super(ScopeKind.declaration, 'declaration'); |
| |
| @override |
| NameSpace get _nameSpace { |
| assert(_declarationBuilder != null, "declarationBuilder has not been set."); |
| return _declarationBuilder!.nameSpace; |
| } |
| |
| void set declarationBuilder(DeclarationBuilder value) { |
| assert(_declarationBuilder == null, |
| "declarationBuilder has already been set."); |
| _declarationBuilder = value; |
| } |
| } |
| |
| /// Computes a builder for the import collision between [declaration] and |
| /// [other]. |
| Builder computeAmbiguousDeclarationForImport(ProblemReporting problemReporting, |
| String name, Builder declaration, Builder other, |
| {required UriOffset uriOffset}) { |
| // Prefix fragments are merged to singular prefix builders when computing the |
| // import scope. |
| assert(!(declaration is PrefixBuilder && other is PrefixBuilder), |
| "Unexpected prefix builders $declaration and $other."); |
| |
| // TODO(ahe): Can I move this to Scope or Prefix? |
| if (declaration == other) return declaration; |
| if (declaration is InvalidTypeDeclarationBuilder) return declaration; |
| if (other is InvalidTypeDeclarationBuilder) return other; |
| Builder? preferred; |
| Uri uri = computeLibraryUri(declaration); |
| Uri otherUri = computeLibraryUri(other); |
| if (declaration is LoadLibraryBuilder) { |
| preferred = declaration; |
| } else if (other is LoadLibraryBuilder) { |
| preferred = other; |
| } else if (otherUri.isScheme("dart") && !uri.isScheme("dart")) { |
| preferred = declaration; |
| } else if (uri.isScheme("dart") && !otherUri.isScheme("dart")) { |
| preferred = other; |
| } |
| if (preferred != null) { |
| return preferred; |
| } |
| |
| Uri firstUri = uri; |
| Uri secondUri = otherUri; |
| if (firstUri.toString().compareTo(secondUri.toString()) > 0) { |
| firstUri = secondUri; |
| secondUri = uri; |
| } |
| Message message = templateDuplicatedImport.withArguments( |
| name, |
| // TODO(ahe): We should probably use a context object here |
| // instead of including URIs in this message. |
| firstUri, |
| secondUri); |
| // We report the error lazily (setting suppressMessage to false) because the |
| // spec 18.1 states that 'It is not an error if N is introduced by two or |
| // more imports but never referred to.' |
| return new InvalidTypeDeclarationBuilder(name, |
| message.withLocation(uriOffset.uri, uriOffset.fileOffset, name.length), |
| suppressMessage: false); |
| } |
| |
| abstract class ProblemBuilder extends BuilderImpl { |
| final String name; |
| |
| final Builder builder; |
| |
| @override |
| final int fileOffset; |
| |
| @override |
| final Uri fileUri; |
| |
| ProblemBuilder(this.name, this.builder, this.fileOffset, this.fileUri); |
| |
| Message get message; |
| |
| @override |
| String get fullNameForErrors => name; |
| } |
| |
| class AmbiguousBuilder extends ProblemBuilder { |
| AmbiguousBuilder(String name, Builder builder, int charOffset, Uri fileUri) |
| : super(name, builder, charOffset, fileUri); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Builder? get parent => null; |
| |
| @override |
| Message get message => templateDuplicatedDeclarationUse.withArguments(name); |
| |
| // TODO(ahe): Also provide context. |
| |
| Builder getFirstDeclaration() { |
| Builder declaration = builder; |
| while (declaration.next != null) { |
| declaration = declaration.next!; |
| } |
| return declaration; |
| } |
| } |
| |
| mixin ErroneousMemberBuilderMixin implements SourceMemberBuilder { |
| @override |
| // Coverage-ignore(suite): Not run. |
| MemberDataForTesting? get dataForTesting => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Iterable<MetadataBuilder>? get metadataForTesting => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Name get memberName => throw new UnsupportedError('$runtimeType.memberName'); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Member? get readTarget => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Reference? get readTargetReference => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Member? get writeTarget => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Reference? get writeTargetReference => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Member? get invokeTarget => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Reference? get invokeTargetReference => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Iterable<Reference> get exportedMemberReferences => const []; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isProperty => throw new UnsupportedError("$runtimeType.isProperty"); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isFinal => false; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isSynthesized => false; |
| |
| @override |
| bool get isConflictingSetter => false; |
| |
| @override |
| bool get isConflictingAugmentationMember => false; |
| |
| @override |
| void set isConflictingAugmentationMember(bool value) { |
| throw new UnsupportedError('$runtimeType.isConflictingAugmentationMember='); |
| } |
| |
| @override |
| DeclarationBuilder get declarationBuilder { |
| throw new UnsupportedError('$runtimeType.declarationBuilder'); |
| } |
| |
| @override |
| ClassBuilder get classBuilder { |
| throw new UnsupportedError('$runtimeType.classBuilder'); |
| } |
| |
| @override |
| SourceLibraryBuilder get libraryBuilder { |
| throw new UnsupportedError('$runtimeType.library'); |
| } |
| |
| @override |
| void buildOutlineExpressions(ClassHierarchy classHierarchy, |
| List<DelayedDefaultValueCloner> delayedDefaultValueCloners) { |
| throw new UnsupportedError('$runtimeType.buildOutlineExpressions'); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void buildOutlineNodes(BuildNodesCallback f) { |
| assert(false, "Unexpected call to $runtimeType.buildOutlineNodes."); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| int buildBodyNodes(BuildNodesCallback f) { |
| assert(false, "Unexpected call to $runtimeType.buildBodyNodes."); |
| return 0; |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| int computeDefaultTypes(ComputeDefaultTypeContext context, |
| {required bool inErrorRecovery}) { |
| assert(false, "Unexpected call to $runtimeType.computeDefaultTypes."); |
| return 0; |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| List<ClassMember> get localMembers => const <ClassMember>[]; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| List<ClassMember> get localSetters => const <ClassMember>[]; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void checkVariance( |
| SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { |
| assert(false, "Unexpected call to $runtimeType.checkVariance."); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void checkTypes(SourceLibraryBuilder library, NameSpace nameSpace, |
| TypeEnvironment typeEnvironment) { |
| assert(false, "Unexpected call to $runtimeType.checkVariance."); |
| } |
| |
| @override |
| bool get isAugmentation { |
| throw new UnsupportedError('$runtimeType.isAugmentation'); |
| } |
| |
| @override |
| AugmentSuperTarget? get augmentSuperTarget { |
| throw new UnsupportedError('$runtimeType.augmentSuperTarget}'); |
| } |
| } |
| |
| class AmbiguousMemberBuilder extends AmbiguousBuilder |
| with ErroneousMemberBuilderMixin { |
| AmbiguousMemberBuilder( |
| String name, Builder builder, int charOffset, Uri fileUri) |
| : super(name, builder, charOffset, fileUri); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Builder get getable => this; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Builder get setable => this; |
| } |
| |
| /// Iterator over builders mapped in a [Scope], including duplicates for each |
| /// directly mapped builder. |
| class ScopeIterator implements Iterator<Builder> { |
| Iterator<Builder>? local; |
| Iterator<Builder>? setters; |
| Iterator<Builder>? extensions; |
| |
| Builder? _current; |
| |
| ScopeIterator(this.local, this.setters, this.extensions); |
| |
| @override |
| bool moveNext() { |
| Builder? next = _current?.next; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (local != null) { |
| if (local!.moveNext()) { |
| _current = local!.current; |
| return true; |
| } |
| local = null; |
| } |
| if (setters != null) { |
| if (setters!.moveNext()) { |
| _current = setters!.current; |
| return true; |
| } |
| setters = null; |
| } |
| if (extensions != null) { |
| while (extensions!.moveNext()) { |
| Builder extension = extensions!.current; |
| // Named extensions have already been included throw [local] so we skip |
| // them here. |
| if (extension is SourceExtensionBuilder && |
| extension.isUnnamedExtension) { |
| _current = extension; |
| return true; |
| } |
| } |
| extensions = null; |
| } |
| _current = null; |
| return false; |
| } |
| |
| @override |
| Builder get current { |
| return _current ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Iterator over builders mapped in a [Scope], including duplicates for each |
| /// directly mapped builder. |
| /// |
| /// Compared to [ScopeIterator] this iterator also gives |
| /// access to the name that the builders are mapped to. |
| class ScopeNameIterator extends ScopeIterator implements NameIterator<Builder> { |
| Iterator<String>? localNames; |
| Iterator<String>? setterNames; |
| |
| String? _name; |
| |
| ScopeNameIterator(Map<String, Builder>? getables, |
| Map<String, Builder>? setables, Iterator<Builder>? extensions) |
| : localNames = getables?.keys.iterator, |
| setterNames = setables?.keys.iterator, |
| super(getables?.values.iterator, setables?.values.iterator, extensions); |
| |
| @override |
| bool moveNext() { |
| Builder? next = _current?.next; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (local != null) { |
| if (local!.moveNext()) { |
| localNames!.moveNext(); |
| _current = local!.current; |
| _name = localNames!.current; |
| return true; |
| } |
| local = null; |
| localNames = null; |
| } |
| if (setters != null) { |
| if (setters!.moveNext()) { |
| setterNames!.moveNext(); |
| _current = setters!.current; |
| _name = setterNames!.current; |
| return true; |
| } |
| setters = null; |
| setterNames = null; |
| } |
| if (extensions != null) { |
| while (extensions!.moveNext()) { |
| Builder extension = extensions!.current; |
| // Named extensions have already been included throw [local] so we skip |
| // them here. |
| if (extension is SourceExtensionBuilder && |
| extension.isUnnamedExtension) { |
| _current = extension; |
| _name = extension.name; |
| return true; |
| } |
| } |
| extensions = null; |
| } |
| _current = null; |
| _name = null; |
| return false; |
| } |
| |
| @override |
| String get name { |
| return _name ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Iterator over builders mapped in a [ConstructorNameSpace], including |
| /// duplicates for each directly mapped builder. |
| class ConstructorNameSpaceIterator implements Iterator<MemberBuilder> { |
| Iterator<MemberBuilder>? _local; |
| |
| MemberBuilder? _current; |
| |
| ConstructorNameSpaceIterator(this._local); |
| |
| @override |
| bool moveNext() { |
| MemberBuilder? next = _current?.next as MemberBuilder?; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (_local != null) { |
| if (_local!.moveNext()) { |
| _current = _local!.current; |
| return true; |
| } |
| _local = null; |
| } |
| return false; |
| } |
| |
| @override |
| MemberBuilder get current { |
| return _current ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Iterator over builders mapped in a [ConstructorNameSpace], including |
| /// duplicates for each directly mapped builder. |
| /// |
| /// Compared to [ConstructorNameSpaceIterator] this iterator also gives |
| /// access to the name that the builders are mapped to. |
| class ConstructorNameSpaceNameIterator extends ConstructorNameSpaceIterator |
| implements NameIterator<MemberBuilder> { |
| Iterator<String>? _localNames; |
| |
| String? _name; |
| |
| ConstructorNameSpaceNameIterator(this._localNames, super.local); |
| |
| @override |
| bool moveNext() { |
| MemberBuilder? next = _current?.next as MemberBuilder?; |
| if (next != null) { |
| _current = next; |
| return true; |
| } |
| if (_local != null) { |
| if (_local!.moveNext()) { |
| _localNames!.moveNext(); |
| _current = _local!.current; |
| _name = _localNames!.current; |
| return true; |
| } |
| _local = null; |
| _localNames = null; |
| } |
| _current = null; |
| _name = null; |
| return false; |
| } |
| |
| @override |
| String get name { |
| return _name ?? // Coverage-ignore(suite): Not run. |
| (throw new StateError('No element')); |
| } |
| } |
| |
| /// Filtered builder [Iterator]. |
| class FilteredIterator<T extends Builder> implements Iterator<T> { |
| final Iterator<Builder> _iterator; |
| final bool includeDuplicates; |
| |
| FilteredIterator(this._iterator, {required this.includeDuplicates}); |
| |
| bool _include(Builder element) { |
| if (!includeDuplicates && |
| (element.isDuplicate || element.isConflictingAugmentationMember)) { |
| return false; |
| } |
| return element is T; |
| } |
| |
| @override |
| T get current => _iterator.current as T; |
| |
| @override |
| bool moveNext() { |
| while (_iterator.moveNext()) { |
| Builder candidate = _iterator.current; |
| if (_include(candidate)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| /// Filtered [NameIterator]. |
| /// |
| /// Compared to [FilteredIterator] this iterator also gives |
| /// access to the name that the builders are mapped to. |
| class FilteredNameIterator<T extends Builder> implements NameIterator<T> { |
| final NameIterator<Builder> _iterator; |
| final bool includeDuplicates; |
| |
| FilteredNameIterator(this._iterator, {required this.includeDuplicates}); |
| |
| bool _include(Builder element) { |
| if (!includeDuplicates && |
| (element.isDuplicate || element.isConflictingAugmentationMember)) { |
| return false; |
| } |
| return element is T; |
| } |
| |
| @override |
| T get current => _iterator.current as T; |
| |
| @override |
| String get name => _iterator.name; |
| |
| @override |
| bool moveNext() { |
| while (_iterator.moveNext()) { |
| Builder candidate = _iterator.current; |
| if (_include(candidate)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| extension IteratorExtension<T extends Builder> on Iterator<T> { |
| void forEach(void Function(T) f) { |
| while (moveNext()) { |
| f(current); |
| } |
| } |
| |
| List<T> toList() { |
| List<T> list = []; |
| while (moveNext()) { |
| list.add(current); |
| } |
| return list; |
| } |
| |
| Iterator<T> join(Iterator<T> other) { |
| return new IteratorSequence<T>([this, other]); |
| } |
| } |
| |
| extension NameIteratorExtension<T extends Builder> on NameIterator<T> { |
| void forEach(void Function(String, T) f) { |
| while (moveNext()) { |
| f(name, current); |
| } |
| } |
| } |
| |
| extension on Builder { |
| bool get isConflictingAugmentationMember { |
| Builder self = this; |
| if (self is SourceMemberBuilder) { |
| return self.isConflictingAugmentationMember; |
| } else if (self is SourceClassBuilder) { |
| return self.isConflictingAugmentationMember; |
| } |
| // TODO(johnniwinther): Handle all cases here. |
| return false; |
| } |
| } |
| |
| class IteratorSequence<T> implements Iterator<T> { |
| Iterator<Iterator<T>> _iterators; |
| |
| Iterator<T>? _current; |
| |
| IteratorSequence(Iterable<Iterator<T>> iterators) |
| : _iterators = iterators.iterator; |
| |
| @override |
| T get current { |
| if (_current != null) { |
| return _current!.current; |
| } |
| // Coverage-ignore-block(suite): Not run. |
| throw new StateError("No current element"); |
| } |
| |
| @override |
| bool moveNext() { |
| if (_current != null) { |
| if (_current!.moveNext()) { |
| return true; |
| } |
| _current = null; |
| } |
| while (_iterators.moveNext()) { |
| _current = _iterators.current; |
| if (_current!.moveNext()) { |
| return true; |
| } |
| _current = null; |
| } |
| return false; |
| } |
| } |