| // Copyright (c) 2024, 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. |
| |
| part of 'source_library_builder.dart'; |
| |
| /// Enum that define what state a source compilation unit is in, in terms of how |
| /// far in the compilation it has progressed. This is used to document and |
| /// assert the requirements of individual methods within the |
| /// [SourceCompilationUnitImpl]. |
| enum SourceCompilationUnitState { |
| initial, |
| importsAddedToScope, |
| ; |
| |
| bool operator <(SourceCompilationUnitState other) => index < other.index; |
| |
| // Coverage-ignore(suite): Not run. |
| bool operator <=(SourceCompilationUnitState other) => index <= other.index; |
| |
| // Coverage-ignore(suite): Not run. |
| bool operator >(SourceCompilationUnitState other) => index > other.index; |
| |
| bool operator >=(SourceCompilationUnitState other) => index >= other.index; |
| } |
| |
| class SourceCompilationUnitImpl implements SourceCompilationUnit { |
| SourceCompilationUnitState _state = SourceCompilationUnitState.initial; |
| |
| @override |
| final Uri fileUri; |
| |
| @override |
| final Uri importUri; |
| |
| final Uri? _packageUri; |
| |
| @override |
| final Uri originImportUri; |
| |
| @override |
| final SourceLoader loader; |
| |
| SourceLibraryBuilder? _libraryBuilder; |
| |
| /// The object used as the root for creating augmentation libraries. |
| // TODO(johnniwinther): Remove this once parts support augmentations. |
| final SourceCompilationUnit? _augmentationRoot; |
| |
| // TODO(johnniwinther): Can we avoid this? |
| final bool? _referenceIsPartOwner; |
| |
| // TODO(johnniwinther): Pass only the [Reference] instead. |
| final LibraryBuilder? _nameOrigin; |
| |
| final LookupScope? _parentScope; |
| |
| /// Map used to find objects created in the [OutlineBuilder] from within |
| /// the [DietListener]. |
| /// |
| /// This is meant to be written once and read once. |
| OffsetMap? _offsetMap; |
| |
| LibraryBuilder? _partOfLibrary; |
| |
| final LibraryProblemReporting _problemReporting; |
| |
| @override |
| final List<Export> exporters = <Export>[]; |
| |
| /// The language version of this library as defined by the language version |
| /// of the package it belongs to, if present, or the current language version |
| /// otherwise. |
| /// |
| /// This language version will be used as the language version for the library |
| /// if the library does not contain an explicit @dart= annotation. |
| @override |
| final LanguageVersion packageLanguageVersion; |
| |
| /// The actual language version of this library. This is initially the |
| /// [packageLanguageVersion] but will be updated if the library contains |
| /// an explicit @dart= language version annotation. |
| LanguageVersion _languageVersion; |
| |
| bool _postponedProblemsIssued = false; |
| List<PostponedProblem>? _postponedProblems; |
| |
| /// Index of the library we use references for. |
| @override |
| final IndexedLibrary? indexedLibrary; |
| |
| late final BuilderFactoryImpl _builderFactory; |
| |
| late final BuilderFactoryResult _builderFactoryResult; |
| |
| final LibraryNameSpaceBuilder _libraryNameSpaceBuilder; |
| |
| final NameSpace _importNameSpace; |
| |
| LibraryFeatures? _libraryFeatures; |
| |
| @override |
| final bool forAugmentationLibrary; |
| |
| @override |
| final bool forPatchLibrary; |
| |
| @override |
| final bool isAugmenting; |
| |
| @override |
| final bool isUnsupported; |
| |
| late final LookupScope _scope; |
| |
| @override |
| final bool mayImplementRestrictedTypes; |
| |
| final Map<String, Builder>? _omittedTypeDeclarationBuilders; |
| |
| factory SourceCompilationUnitImpl( |
| {required Uri importUri, |
| required Uri fileUri, |
| required Uri? packageUri, |
| required LanguageVersion packageLanguageVersion, |
| required Uri originImportUri, |
| required IndexedLibrary? indexedLibrary, |
| Map<String, Builder>? omittedTypeDeclarationBuilders, |
| LookupScope? parentScope, |
| required bool forAugmentationLibrary, |
| required SourceCompilationUnit? augmentationRoot, |
| required LibraryBuilder? nameOrigin, |
| required bool? referenceIsPartOwner, |
| required bool forPatchLibrary, |
| required bool isAugmenting, |
| required bool isUnsupported, |
| required SourceLoader loader, |
| required bool mayImplementRestrictedTypes}) { |
| LibraryNameSpaceBuilder libraryNameSpaceBuilder = |
| new LibraryNameSpaceBuilder(); |
| NameSpace importNameSpace = new NameSpaceImpl(); |
| return new SourceCompilationUnitImpl._(libraryNameSpaceBuilder, |
| importUri: importUri, |
| fileUri: fileUri, |
| packageUri: packageUri, |
| packageLanguageVersion: packageLanguageVersion, |
| originImportUri: originImportUri, |
| indexedLibrary: indexedLibrary, |
| omittedTypeDeclarationBuilders: omittedTypeDeclarationBuilders, |
| parentScope: parentScope, |
| importNameSpace: importNameSpace, |
| forAugmentationLibrary: forAugmentationLibrary, |
| augmentationRoot: augmentationRoot, |
| nameOrigin: nameOrigin, |
| referenceIsPartOwner: referenceIsPartOwner, |
| forPatchLibrary: forPatchLibrary, |
| isAugmenting: isAugmenting, |
| isUnsupported: isUnsupported, |
| loader: loader, |
| mayImplementRestrictedTypes: mayImplementRestrictedTypes); |
| } |
| |
| SourceCompilationUnitImpl._(LibraryNameSpaceBuilder libraryNameSpaceBuilder, |
| {required this.importUri, |
| required this.fileUri, |
| required Uri? packageUri, |
| required this.packageLanguageVersion, |
| required this.originImportUri, |
| required this.indexedLibrary, |
| Map<String, Builder>? omittedTypeDeclarationBuilders, |
| LookupScope? parentScope, |
| required NameSpace importNameSpace, |
| required this.forAugmentationLibrary, |
| required SourceCompilationUnit? augmentationRoot, |
| required LibraryBuilder? nameOrigin, |
| required bool? referenceIsPartOwner, |
| required this.forPatchLibrary, |
| required this.isAugmenting, |
| required this.isUnsupported, |
| required this.loader, |
| required this.mayImplementRestrictedTypes}) |
| : _languageVersion = packageLanguageVersion, |
| _packageUri = packageUri, |
| _libraryNameSpaceBuilder = libraryNameSpaceBuilder, |
| _importNameSpace = importNameSpace, |
| _augmentationRoot = augmentationRoot, |
| _nameOrigin = nameOrigin, |
| _parentScope = parentScope, |
| _referenceIsPartOwner = referenceIsPartOwner, |
| _omittedTypeDeclarationBuilders = omittedTypeDeclarationBuilders, |
| _problemReporting = new LibraryProblemReporting(loader, fileUri) { |
| _scope = new SourceLibraryBuilderScope( |
| this, ScopeKind.typeParameters, 'library'); |
| |
| // TODO(johnniwinther): Create these in [createOutlineBuilder]. |
| _builderFactoryResult = _builderFactory = new BuilderFactoryImpl( |
| compilationUnit: this, |
| augmentationRoot: augmentationRoot ?? this, |
| libraryNameSpaceBuilder: libraryNameSpaceBuilder, |
| problemReporting: _problemReporting, |
| scope: _scope, |
| indexedLibrary: indexedLibrary, |
| omittedTypeDeclarationBuilders: omittedTypeDeclarationBuilders); |
| } |
| |
| SourceCompilationUnitState get state => _state; |
| |
| void set state(SourceCompilationUnitState value) { |
| assert(_state < value, |
| "State $value has already been reached at $_state in $this."); |
| assert( |
| _state.index + 1 == value.index, |
| _state.index + 1 < SourceCompilationUnitState.values.length |
| ? "Expected state " |
| "${SourceCompilationUnitState.values[_state.index + 1]} " |
| "to follow from $_state, trying to set next state to $value " |
| "in $this." |
| : "No more states expected to follow from $_state, trying to set " |
| "next state to $value in $this."); |
| _state = value; |
| } |
| |
| bool checkState( |
| {List<SourceCompilationUnitState>? required, |
| List<SourceCompilationUnitState>? pending}) { |
| if (required != null) { |
| for (SourceCompilationUnitState requiredState in required) { |
| assert(state >= requiredState, |
| "State $requiredState required, but found $state in $this."); |
| } |
| } |
| if (pending != null) { |
| // Coverage-ignore-block(suite): Not run. |
| for (SourceCompilationUnitState pendingState in pending) { |
| assert( |
| state < pendingState, |
| "State $pendingState must not have been reached, " |
| "but found $state in $this."); |
| } |
| } |
| return true; |
| } |
| |
| @override |
| LibraryFeatures get libraryFeatures => |
| _libraryFeatures ??= new LibraryFeatures(loader.target.globalFeatures, |
| _packageUri ?? originImportUri, languageVersion.version); |
| |
| @override |
| bool get isDartLibrary => |
| originImportUri.isScheme("dart") || fileUri.isScheme("org-dartlang-sdk"); |
| |
| /// Returns the map of objects created in the [OutlineBuilder]. |
| /// |
| /// This should only be called once. |
| @override |
| OffsetMap get offsetMap { |
| assert(_offsetMap != null, "No OffsetMap for $this"); |
| OffsetMap map = _offsetMap!; |
| _offsetMap = null; |
| return map; |
| } |
| |
| @override |
| SourceLibraryBuilder get libraryBuilder { |
| assert(_libraryBuilder != null, |
| "Library builder for $this has not been computed yet."); |
| return _libraryBuilder!; |
| } |
| |
| @override |
| void addExporter(CompilationUnit exporter, |
| List<CombinatorBuilder>? combinators, int charOffset) { |
| exporters.add(new Export(exporter, this, combinators, charOffset)); |
| } |
| |
| @override |
| void addProblem(Message message, int charOffset, int length, Uri? fileUri, |
| {bool wasHandled = false, |
| List<LocatedMessage>? context, |
| Severity? severity, |
| bool problemOnLibrary = false}) { |
| _problemReporting.addProblem(message, charOffset, length, fileUri, |
| wasHandled: wasHandled, |
| context: context, |
| severity: severity, |
| problemOnLibrary: problemOnLibrary); |
| } |
| |
| @override |
| final List<LibraryAccess> accessors = []; |
| |
| @override |
| Message? accessProblem; |
| |
| @override |
| void addProblemAtAccessors(Message message) { |
| if (accessProblem == null) { |
| if (accessors.isEmpty && |
| // Coverage-ignore(suite): Not run. |
| loader.roots.contains(this.importUri)) { |
| // Coverage-ignore-block(suite): Not run. |
| // This is the entry point library, and nobody access it directly. So |
| // we need to report a problem. |
| loader.addProblem(message, -1, 1, null); |
| } |
| for (int i = 0; i < accessors.length; i++) { |
| LibraryAccess access = accessors[i]; |
| access.accessor.addProblem( |
| message, access.charOffset, access.length, access.fileUri); |
| } |
| accessProblem = message; |
| } |
| } |
| |
| @override |
| LanguageVersion get languageVersion { |
| assert( |
| _languageVersion.isFinal, |
| "Attempting to read the language version of ${this} before has been " |
| "finalized."); |
| return _languageVersion; |
| } |
| |
| @override |
| void markLanguageVersionFinal() { |
| _languageVersion.isFinal = true; |
| } |
| |
| /// Set the language version to an explicit major and minor version. |
| /// |
| /// The default language version specified by the `package_config.json` file |
| /// is passed to the constructor, but the library can have source code that |
| /// specifies another one which should be supported. |
| /// |
| /// Only the first registered language version is used. |
| /// |
| /// [offset] and [length] refers to the offset and length of the source code |
| /// specifying the language version. |
| @override |
| void registerExplicitLanguageVersion(Version version, |
| {int offset = 0, int length = noLength}) { |
| if (_languageVersion.isExplicit) { |
| // If more than once language version exists we use the first. |
| return; |
| } |
| assert(!_languageVersion.isFinal); |
| |
| if (version > loader.target.currentSdkVersion) { |
| // Coverage-ignore-block(suite): Not run. |
| // If trying to set a language version that is higher than the current sdk |
| // version it's an error. |
| addPostponedProblem( |
| templateLanguageVersionTooHigh.withArguments( |
| loader.target.currentSdkVersion.major, |
| loader.target.currentSdkVersion.minor), |
| offset, |
| length, |
| fileUri); |
| // If the package set an OK version, but the file set an invalid version |
| // we want to use the package version. |
| _languageVersion = new InvalidLanguageVersion( |
| fileUri, offset, length, packageLanguageVersion.version, true); |
| } else if (version < loader.target.leastSupportedVersion) { |
| addPostponedProblem( |
| templateLanguageVersionTooLow.withArguments( |
| loader.target.leastSupportedVersion.major, |
| loader.target.leastSupportedVersion.minor), |
| offset, |
| length, |
| fileUri); |
| _languageVersion = new InvalidLanguageVersion( |
| fileUri, offset, length, loader.target.leastSupportedVersion, true); |
| } else { |
| _languageVersion = new LanguageVersion(version, fileUri, offset, length); |
| } |
| _languageVersion.isFinal = true; |
| } |
| |
| @override |
| void addPostponedProblem( |
| Message message, int charOffset, int length, Uri fileUri) { |
| if (_postponedProblemsIssued) { |
| // Coverage-ignore-block(suite): Not run. |
| addProblem(message, charOffset, length, fileUri); |
| } else { |
| _postponedProblems ??= <PostponedProblem>[]; |
| _postponedProblems! |
| .add(new PostponedProblem(message, charOffset, length, fileUri)); |
| } |
| } |
| |
| @override |
| void issuePostponedProblems() { |
| _postponedProblemsIssued = true; |
| if (_postponedProblems == null) return; |
| for (int i = 0; i < _postponedProblems!.length; ++i) { |
| PostponedProblem postponedProblem = _postponedProblems![i]; |
| addProblem(postponedProblem.message, postponedProblem.charOffset, |
| postponedProblem.length, postponedProblem.fileUri); |
| } |
| _postponedProblems = null; |
| } |
| |
| @override |
| Iterable<Uri> get dependencies sync* { |
| for (Export export in _builderFactoryResult.exports) { |
| yield export.exportedCompilationUnit.importUri; |
| } |
| for (Import import in _builderFactoryResult.imports) { |
| CompilationUnit? imported = import.importedCompilationUnit; |
| if (imported != null) { |
| yield imported.importUri; |
| } |
| } |
| } |
| |
| @override |
| bool get isPart => _builderFactoryResult.isPart; |
| |
| @override |
| bool get isSynthetic => accessProblem != null; |
| |
| @override |
| LibraryBuilder? get partOfLibrary => _partOfLibrary; |
| |
| @override |
| void recordAccess( |
| CompilationUnit accessor, int charOffset, int length, Uri fileUri) { |
| accessors.add(new LibraryAccess(accessor, fileUri, charOffset, length)); |
| if (accessProblem != null) { |
| // Coverage-ignore-block(suite): Not run. |
| addProblem(accessProblem!, charOffset, length, fileUri); |
| } |
| } |
| |
| @override |
| OutlineBuilder createOutlineBuilder() { |
| assert(_offsetMap == null, "OffsetMap has already been set for $this"); |
| return new OutlineBuilder( |
| this, _builderFactory, _offsetMap = new OffsetMap(fileUri)); |
| } |
| |
| @override |
| SourceLibraryBuilder createLibrary([Library? library]) { |
| assert(_libraryBuilder == null, |
| "Source library builder as already been created for $this."); |
| SourceLibraryBuilder libraryBuilder = _libraryBuilder = |
| new SourceLibraryBuilder( |
| compilationUnit: this, |
| importUri: importUri, |
| fileUri: fileUri, |
| packageUri: _packageUri, |
| originImportUri: originImportUri, |
| packageLanguageVersion: packageLanguageVersion, |
| loader: loader, |
| nameOrigin: _nameOrigin, |
| origin: _augmentationRoot?.libraryBuilder, |
| target: library, |
| indexedLibrary: indexedLibrary, |
| referenceIsPartOwner: _referenceIsPartOwner, |
| isUnsupported: isUnsupported, |
| isAugmentation: forAugmentationLibrary, |
| isPatch: forPatchLibrary, |
| parentScope: _parentScope, |
| importNameSpace: _importNameSpace, |
| libraryNameSpaceBuilder: _libraryNameSpaceBuilder, |
| omittedTypes: _omittedTypeDeclarationBuilders); |
| _problemReporting.registerLibrary(libraryBuilder.library); |
| if (isPart) { |
| // Coverage-ignore-block(suite): Not run. |
| // This is a part with no enclosing library. |
| addProblem(messagePartOrphan, 0, 1, fileUri); |
| _clearPartsAndReportExporters(); |
| } |
| return libraryBuilder; |
| } |
| |
| @override |
| String toString() => 'SourceCompilationUnitImpl($fileUri)'; |
| |
| void _addNativeDependency(Library library, String nativeImportPath) { |
| MemberBuilder constructor = loader.getNativeAnnotation(); |
| Arguments arguments = |
| new Arguments(<Expression>[new StringLiteral(nativeImportPath)]); |
| Expression annotation; |
| if (constructor.isConstructor) { |
| annotation = new ConstructorInvocation( |
| constructor.member as Constructor, arguments) |
| ..isConst = true; |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| annotation = |
| new StaticInvocation(constructor.member as Procedure, arguments) |
| ..isConst = true; |
| } |
| library.addAnnotation(annotation); |
| } |
| |
| @override |
| void addDependencies(Library library, Set<SourceCompilationUnit> seen) { |
| assert( |
| checkState(required: [SourceCompilationUnitState.importsAddedToScope])); |
| |
| if (!seen.add(this)) { |
| return; |
| } |
| |
| for (Import import in _builderFactoryResult.imports) { |
| // Rather than add a LibraryDependency, we attach an annotation. |
| if (import.nativeImportPath != null) { |
| _addNativeDependency(library, import.nativeImportPath!); |
| continue; |
| } |
| |
| LibraryDependency libraryDependency; |
| if (import.deferred && |
| import.prefixFragment?.builder.dependency != null) { |
| libraryDependency = import.prefixFragment!.builder.dependency!; |
| } else { |
| LibraryBuilder imported = import.importedLibraryBuilder!.origin; |
| Library targetLibrary = imported.library; |
| libraryDependency = new LibraryDependency.import(targetLibrary, |
| name: import.prefix, |
| combinators: toKernelCombinators(import.combinators)) |
| ..fileOffset = import.importOffset; |
| } |
| library.addDependency(libraryDependency); |
| import.libraryDependency = libraryDependency; |
| } |
| for (Export export in _builderFactoryResult.exports) { |
| LibraryDependency libraryDependency = new LibraryDependency.export( |
| export.exportedLibraryBuilder.library, |
| combinators: toKernelCombinators(export.combinators)) |
| ..fileOffset = export.charOffset; |
| library.addDependency(libraryDependency); |
| export.libraryDependency = libraryDependency; |
| } |
| } |
| |
| @override |
| String? get partOfName => _builderFactoryResult.partOfName; |
| |
| @override |
| Uri? get partOfUri => _builderFactoryResult.partOfUri; |
| |
| @override |
| LookupScope get scope => _scope; |
| |
| @override |
| void takeMixinApplications( |
| Map<SourceClassBuilder, TypeBuilder> mixinApplications) { |
| _builderFactoryResult.takeMixinApplications(mixinApplications); |
| } |
| |
| @override |
| void includeParts(SourceLibraryBuilder libraryBuilder, |
| List<SourceCompilationUnit> includedParts, Set<Uri> usedParts) { |
| Set<Uri> seenParts = new Set<Uri>(); |
| int index = 0; |
| while (index < _builderFactoryResult.parts.length) { |
| Part part = _builderFactoryResult.parts[index]; |
| bool keepPart = true; |
| // TODO(johnniwinther): Use [part.offset] in messages. |
| if (part.compilationUnit == this) { |
| addProblem(messagePartOfSelf, -1, noLength, fileUri); |
| keepPart = false; |
| } else if (seenParts.add(part.compilationUnit.fileUri)) { |
| if (part.compilationUnit.partOfLibrary != null) { |
| addProblem(messagePartOfTwoLibraries, -1, noLength, |
| part.compilationUnit.fileUri, |
| context: [ |
| messagePartOfTwoLibrariesContext.withLocation( |
| part.compilationUnit.partOfLibrary!.fileUri, -1, noLength), |
| messagePartOfTwoLibrariesContext.withLocation( |
| fileUri, -1, noLength) |
| ]); |
| keepPart = false; |
| } else { |
| usedParts.add(part.compilationUnit.importUri); |
| keepPart = _includePart(libraryBuilder, this, includedParts, |
| part.compilationUnit, usedParts, part.offset); |
| } |
| } else { |
| addProblem( |
| templatePartTwice.withArguments(part.compilationUnit.fileUri), |
| -1, |
| noLength, |
| fileUri); |
| keepPart = false; |
| } |
| if (keepPart) { |
| index++; |
| } else { |
| // TODO(johnniwinther): Stop removing parts. |
| _builderFactoryResult.parts.removeAt(index); |
| } |
| } |
| } |
| |
| bool _includePart( |
| SourceLibraryBuilder libraryBuilder, |
| SourceCompilationUnit parentCompilationUnit, |
| List<SourceCompilationUnit> includedParts, |
| CompilationUnit part, |
| Set<Uri> usedParts, |
| int partOffset) { |
| switch (part) { |
| case SourceCompilationUnit(): |
| if (part.partOfUri != null) { |
| if (isNotMalformedUriScheme(part.partOfUri!) && |
| part.partOfUri != parentCompilationUnit.importUri) { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| parentCompilationUnit.addProblem( |
| templatePartOfUriMismatch.withArguments(part.fileUri, |
| parentCompilationUnit.importUri, part.partOfUri!), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri); |
| return false; |
| } |
| } else if (part.partOfName != null) { |
| if (parentCompilationUnit.name != null) { |
| if (part.partOfName != parentCompilationUnit.name) { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| parentCompilationUnit.addProblem( |
| templatePartOfLibraryNameMismatch.withArguments(part.fileUri, |
| parentCompilationUnit.name!, part.partOfName!), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri); |
| return false; |
| } |
| } else { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| parentCompilationUnit.addProblem( |
| templatePartOfUseUri.withArguments(part.fileUri, |
| parentCompilationUnit.fileUri, part.partOfName!), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri); |
| return false; |
| } |
| } else { |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| assert(!part.isPart); |
| if (isNotMalformedUriScheme(part.fileUri)) { |
| parentCompilationUnit.addProblem( |
| templateMissingPartOf.withArguments(part.fileUri), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri); |
| } |
| return false; |
| } |
| |
| // Language versions have to match. Except if (at least) one of them is |
| // invalid in which case we've already gotten an error about this. |
| if (parentCompilationUnit.languageVersion != part.languageVersion && |
| // Coverage-ignore(suite): Not run. |
| parentCompilationUnit.languageVersion.valid && |
| // Coverage-ignore(suite): Not run. |
| part.languageVersion.valid) { |
| // Coverage-ignore-block(suite): Not run. |
| // This is an error, but the part is not removed from the list of |
| // parts, so that metadata annotations can be associated with it. |
| List<LocatedMessage> context = <LocatedMessage>[]; |
| if (parentCompilationUnit.languageVersion.isExplicit) { |
| context.add(messageLanguageVersionLibraryContext.withLocation( |
| parentCompilationUnit.languageVersion.fileUri!, |
| parentCompilationUnit.languageVersion.charOffset, |
| parentCompilationUnit.languageVersion.charCount)); |
| } |
| if (part.languageVersion.isExplicit) { |
| context.add(messageLanguageVersionPartContext.withLocation( |
| part.languageVersion.fileUri!, |
| part.languageVersion.charOffset, |
| part.languageVersion.charCount)); |
| } |
| parentCompilationUnit.addProblem(messageLanguageVersionMismatchInPart, |
| partOffset, noLength, parentCompilationUnit.fileUri, |
| context: context); |
| } |
| |
| part.validatePart(libraryBuilder, _libraryNameSpaceBuilder, usedParts); |
| includedParts.add(part); |
| return true; |
| case DillCompilationUnit(): |
| // Trying to add a dill library builder as a part means that it exists |
| // as a stand-alone library in the dill file. |
| // This means, that it's not a part (if it had been it would be been |
| // "merged in" to the real library and thus not been a library on its |
| // own) so we behave like if it's a library with a missing "part of" |
| // declaration (i.e. as it was a SourceLibraryBuilder without a |
| // "part of" declaration). |
| if (isNotMalformedUriScheme(part.fileUri)) { |
| parentCompilationUnit.addProblem( |
| templateMissingPartOf.withArguments(part.fileUri), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri); |
| } |
| return false; |
| } |
| } |
| |
| void _becomePart(SourceLibraryBuilder libraryBuilder, |
| LibraryNameSpaceBuilder libraryNameSpaceBuilder) { |
| libraryNameSpaceBuilder.includeBuilders(_libraryNameSpaceBuilder); |
| |
| // TODO(ahe): Include metadata from part? |
| |
| // Recovery: Take on all exporters (i.e. if a library has erroneously |
| // exported the part it has (in validatePart) been recovered to import |
| // the main library (this) instead --- to make it complete (and set up |
| // scopes correctly) the exporters in this has to be updated too). |
| libraryBuilder.exporters.addAll(exporters); |
| |
| // Check that the targets are different. This is not normally a problem |
| // but is for augmentation libraries. |
| |
| _problemReporting.registerLibrary(libraryBuilder.library); |
| } |
| |
| @override |
| int resolveTypes(ProblemReporting problemReporting) { |
| return _builderFactoryResult.typeScope.resolveTypes(problemReporting); |
| } |
| |
| @override |
| int finishNativeMethods() { |
| return _builderFactoryResult.finishNativeMethods(); |
| } |
| |
| void _clearPartsAndReportExporters() { |
| assert(_libraryBuilder != null, "Library has not be set."); |
| _builderFactoryResult.parts.clear(); |
| if (exporters.isNotEmpty) { |
| // Coverage-ignore-block(suite): Not run. |
| List<LocatedMessage> context = <LocatedMessage>[ |
| messagePartExportContext.withLocation(fileUri, -1, 1), |
| ]; |
| for (Export export in exporters) { |
| export.exporter.addProblem( |
| messagePartExport, export.charOffset, "export".length, null, |
| context: context); |
| } |
| } |
| } |
| |
| @override |
| void validatePart(SourceLibraryBuilder libraryBuilder, |
| LibraryNameSpaceBuilder libraryNameSpaceBuilder, Set<Uri>? usedParts) { |
| _libraryBuilder = libraryBuilder; |
| _partOfLibrary = libraryBuilder; |
| if (_builderFactoryResult.parts.isNotEmpty) { |
| List<LocatedMessage> context = <LocatedMessage>[ |
| messagePartInPartLibraryContext.withLocation( |
| libraryBuilder.fileUri, -1, 1), |
| ]; |
| for (Part part in _builderFactoryResult.parts) { |
| addProblem(messagePartInPart, part.offset, noLength, fileUri, |
| context: context); |
| // Mark this part as used so we don't report it as orphaned. |
| usedParts!.add(part.compilationUnit.importUri); |
| } |
| } |
| _clearPartsAndReportExporters(); |
| _becomePart(libraryBuilder, libraryNameSpaceBuilder); |
| } |
| |
| @override |
| void collectUnboundTypeVariables( |
| SourceLibraryBuilder libraryBuilder, |
| Map<NominalVariableBuilder, SourceLibraryBuilder> nominalVariables, |
| Map<StructuralVariableBuilder, SourceLibraryBuilder> |
| structuralVariables) { |
| _builderFactoryResult.collectUnboundTypeVariables( |
| libraryBuilder, nominalVariables, structuralVariables); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void addSyntheticImport( |
| {required String uri, |
| required String? prefix, |
| required List<CombinatorBuilder>? combinators, |
| required bool deferred}) { |
| assert( |
| checkState(pending: [SourceCompilationUnitState.importsAddedToScope])); |
| _builderFactory.addImport( |
| metadata: null, |
| isAugmentationImport: false, |
| uri: uri, |
| configurations: null, |
| prefix: prefix, |
| combinators: combinators, |
| deferred: deferred, |
| charOffset: -1, |
| prefixCharOffset: -1, |
| uriOffset: -1); |
| } |
| |
| @override |
| void addImportsToScope() { |
| assert(checkState(required: [SourceCompilationUnitState.initial])); |
| |
| bool hasCoreImport = originImportUri == dartCore && |
| // Coverage-ignore(suite): Not run. |
| !forPatchLibrary; |
| for (Import import in _builderFactoryResult.imports) { |
| if (import.importedCompilationUnit?.isPart ?? false) { |
| // Coverage-ignore-block(suite): Not run. |
| addProblem( |
| templatePartOfInLibrary |
| .withArguments(import.importedCompilationUnit!.fileUri), |
| import.importOffset, |
| noLength, |
| fileUri); |
| } |
| if (import.importedLibraryBuilder == loader.coreLibrary) { |
| hasCoreImport = true; |
| } |
| import.finalizeImports(this); |
| } |
| if (!hasCoreImport) { |
| NameIterator<Builder> iterator = loader.coreLibrary.exportNameSpace |
| .filteredNameIterator( |
| includeDuplicates: false, includeAugmentations: false); |
| while (iterator.moveNext()) { |
| addImportedBuilderToScope( |
| name: iterator.name, builder: iterator.current, charOffset: -1); |
| } |
| } |
| |
| state = SourceCompilationUnitState.importsAddedToScope; |
| } |
| |
| @override |
| void addImportedBuilderToScope( |
| {required String name, |
| required Builder builder, |
| required int charOffset}) { |
| Builder? existing = |
| _importNameSpace.lookupLocalMember(name, setter: builder.isSetter); |
| if (existing != null) { |
| if (existing != builder) { |
| _importNameSpace.addLocalMember( |
| name, |
| computeAmbiguousDeclarationForImport( |
| _problemReporting, name, existing, builder, |
| uriOffset: new UriOffset(fileUri, charOffset)), |
| setter: builder.isSetter); |
| } |
| } else { |
| _importNameSpace.addLocalMember(name, builder, setter: builder.isSetter); |
| } |
| if (builder.isExtension) { |
| _importNameSpace.addExtension(builder as ExtensionBuilder); |
| } |
| } |
| |
| @override |
| void buildOutlineNode(Library library) { |
| library.setLanguageVersion(_languageVersion.version); |
| switch (loader.nnbdMode) { |
| case NnbdMode.Weak: |
| library.nonNullableByDefaultCompiledMode = |
| NonNullableByDefaultCompiledMode.Weak; |
| break; |
| case NnbdMode.Strong: |
| library.nonNullableByDefaultCompiledMode = |
| NonNullableByDefaultCompiledMode.Strong; |
| break; |
| } |
| for (LibraryPart libraryPart in _builderFactoryResult.libraryParts) { |
| library.addPart(libraryPart); |
| } |
| } |
| |
| @override |
| int finishDeferredLoadTearOffs(Library library) { |
| assert( |
| checkState(required: [SourceCompilationUnitState.importsAddedToScope])); |
| |
| int total = 0; |
| for (Import import in _builderFactoryResult.imports) { |
| if (import.deferred) { |
| Procedure? tearoff = |
| import.prefixFragment!.builder.loadLibraryBuilder?.tearoff; |
| // In case of conflict between deferred and non-deferred prefixes of |
| // the same name, the [PrefixBuilder] might not have a load library |
| // function. |
| if (tearoff != null) { |
| library.addProcedure(tearoff); |
| } |
| total++; |
| } |
| } |
| return total; |
| } |
| |
| @override |
| List<MetadataBuilder>? get metadata => _builderFactoryResult.metadata; |
| |
| @override |
| String? get name => _builderFactoryResult.name; |
| |
| @override |
| int computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder nullType, |
| TypeBuilder bottomType, ClassBuilder objectClass) { |
| int count = 0; |
| |
| int computeDefaultTypesForVariables(List<NominalVariableBuilder>? variables, |
| {required bool inErrorRecovery}) { |
| if (variables == null) return 0; |
| |
| bool haveErroneousBounds = false; |
| if (!inErrorRecovery) { |
| if (!libraryFeatures.genericMetadata.isEnabled) { |
| for (NominalVariableBuilder variable in variables) { |
| haveErroneousBounds = |
| _recursivelyReportGenericFunctionTypesAsBoundsForVariable( |
| variable) || |
| haveErroneousBounds; |
| } |
| } |
| |
| if (!haveErroneousBounds) { |
| List<StructuralVariableBuilder> unboundTypeVariables = []; |
| List<TypeBuilder> calculatedBounds = calculateBounds( |
| variables, dynamicType, bottomType, |
| unboundTypeVariables: unboundTypeVariables); |
| _builderFactoryResult |
| .registerUnresolvedStructuralVariables(unboundTypeVariables); |
| |
| for (int i = 0; i < variables.length; ++i) { |
| variables[i].defaultType = calculatedBounds[i]; |
| } |
| } |
| } |
| |
| if (inErrorRecovery || haveErroneousBounds) { |
| // Use dynamic in case of errors. |
| for (int i = 0; i < variables.length; ++i) { |
| variables[i].defaultType = dynamicType; |
| } |
| } |
| |
| return variables.length; |
| } |
| |
| void reportIssues(List<NonSimplicityIssue> issues) { |
| for (NonSimplicityIssue issue in issues) { |
| addProblem(issue.message, issue.declaration.charOffset, |
| issue.declaration.name.length, issue.declaration.fileUri, |
| context: issue.context); |
| } |
| } |
| |
| void processSourceProcedureBuilder(SourceProcedureBuilder member) { |
| List<NonSimplicityIssue> issues = |
| getNonSimplicityIssuesForTypeVariables(member.typeVariables); |
| if (member.formals != null && member.formals!.isNotEmpty) { |
| for (FormalParameterBuilder formal in member.formals!) { |
| issues.addAll(getInboundReferenceIssuesInType(formal.type)); |
| _recursivelyReportGenericFunctionTypesAsBoundsForType(formal.type); |
| } |
| } |
| if (member.returnType is! OmittedTypeBuilder) { |
| issues.addAll(getInboundReferenceIssuesInType(member.returnType)); |
| _recursivelyReportGenericFunctionTypesAsBoundsForType( |
| member.returnType); |
| } |
| reportIssues(issues); |
| count += computeDefaultTypesForVariables(member.typeVariables, |
| inErrorRecovery: issues.isNotEmpty); |
| } |
| |
| void processSourceFieldBuilder(SourceFieldBuilder member) { |
| TypeBuilder? fieldType = member.type; |
| if (fieldType is! OmittedTypeBuilder) { |
| List<NonSimplicityIssue> issues = |
| getInboundReferenceIssuesInType(fieldType); |
| reportIssues(issues); |
| _recursivelyReportGenericFunctionTypesAsBoundsForType(fieldType); |
| } |
| } |
| |
| void processSourceConstructorBuilder(SourceFunctionBuilder member, |
| {required bool inErrorRecovery}) { |
| count += computeDefaultTypesForVariables(member.typeVariables, |
| // Type variables are inherited from the enclosing declaration, so if |
| // it has issues, so do the constructors. |
| inErrorRecovery: inErrorRecovery); |
| List<FormalParameterBuilder>? formals = member.formals; |
| if (formals != null && formals.isNotEmpty) { |
| for (FormalParameterBuilder formal in formals) { |
| List<NonSimplicityIssue> issues = |
| getInboundReferenceIssuesInType(formal.type); |
| reportIssues(issues); |
| _recursivelyReportGenericFunctionTypesAsBoundsForType(formal.type); |
| } |
| } |
| } |
| |
| void processSourceMemberBuilder(SourceMemberBuilder member, |
| {required bool inErrorRecovery}) { |
| if (member is SourceProcedureBuilder) { |
| processSourceProcedureBuilder(member); |
| } else if (member is SourceFieldBuilder) { |
| processSourceFieldBuilder(member); |
| } else { |
| assert(member is SourceFactoryBuilder || |
| member is SourceConstructorBuilder); |
| processSourceConstructorBuilder(member as SourceFunctionBuilder, |
| inErrorRecovery: inErrorRecovery); |
| } |
| } |
| |
| void computeDefaultValuesForDeclaration(Builder declaration) { |
| if (declaration is SourceClassBuilder) { |
| List<NonSimplicityIssue> issues = getNonSimplicityIssuesForDeclaration( |
| declaration, |
| performErrorRecovery: true); |
| reportIssues(issues); |
| count += computeDefaultTypesForVariables(declaration.typeVariables, |
| inErrorRecovery: issues.isNotEmpty); |
| |
| Iterator<SourceMemberBuilder> iterator = declaration.nameSpace |
| .filteredConstructorIterator<SourceMemberBuilder>( |
| parent: declaration, |
| includeDuplicates: false, |
| includeAugmentations: true); |
| while (iterator.moveNext()) { |
| processSourceMemberBuilder(iterator.current, |
| inErrorRecovery: issues.isNotEmpty); |
| } |
| |
| Iterator<SourceMemberBuilder> memberIterator = |
| declaration.fullMemberIterator<SourceMemberBuilder>(); |
| while (memberIterator.moveNext()) { |
| SourceMemberBuilder member = memberIterator.current; |
| processSourceMemberBuilder(member, |
| inErrorRecovery: issues.isNotEmpty); |
| } |
| } else if (declaration is SourceTypeAliasBuilder) { |
| List<NonSimplicityIssue> issues = getNonSimplicityIssuesForDeclaration( |
| declaration, |
| performErrorRecovery: true); |
| issues.addAll(getInboundReferenceIssuesInType(declaration.type)); |
| reportIssues(issues); |
| count += computeDefaultTypesForVariables(declaration.typeVariables, |
| inErrorRecovery: issues.isNotEmpty); |
| _recursivelyReportGenericFunctionTypesAsBoundsForType(declaration.type); |
| } else if (declaration is SourceMemberBuilder) { |
| processSourceMemberBuilder(declaration, inErrorRecovery: false); |
| } else if (declaration is SourceExtensionBuilder) { |
| List<NonSimplicityIssue> issues = getNonSimplicityIssuesForDeclaration( |
| declaration, |
| performErrorRecovery: true); |
| reportIssues(issues); |
| count += computeDefaultTypesForVariables(declaration.typeParameters, |
| inErrorRecovery: issues.isNotEmpty); |
| |
| declaration.forEach((String name, Builder member) { |
| if (member is SourceMemberBuilder) { |
| processSourceMemberBuilder(member, |
| inErrorRecovery: issues.isNotEmpty); |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| assert(false, |
| "Unexpected extension member $member (${member.runtimeType})."); |
| } |
| }); |
| } else if (declaration is SourceExtensionTypeDeclarationBuilder) { |
| List<NonSimplicityIssue> issues = getNonSimplicityIssuesForDeclaration( |
| declaration, |
| performErrorRecovery: true); |
| reportIssues(issues); |
| count += computeDefaultTypesForVariables(declaration.typeParameters, |
| inErrorRecovery: issues.isNotEmpty); |
| |
| Iterator<SourceMemberBuilder> iterator = declaration.nameSpace |
| .filteredConstructorIterator<SourceMemberBuilder>( |
| parent: declaration, |
| includeDuplicates: false, |
| includeAugmentations: true); |
| while (iterator.moveNext()) { |
| processSourceMemberBuilder(iterator.current, |
| inErrorRecovery: issues.isNotEmpty); |
| } |
| |
| declaration.forEach((String name, Builder member) { |
| if (member is SourceMemberBuilder) { |
| processSourceMemberBuilder(member, |
| inErrorRecovery: issues.isNotEmpty); |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| assert( |
| false, |
| "Unexpected extension type member " |
| "$member (${member.runtimeType})."); |
| } |
| }); |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| assert( |
| declaration is PrefixBuilder || |
| declaration is DynamicTypeDeclarationBuilder || |
| declaration is NeverTypeDeclarationBuilder, |
| "Unexpected top level member $declaration " |
| "(${declaration.runtimeType})."); |
| } |
| } |
| |
| Iterator<Builder> iterator = libraryBuilder.localMembersIterator; |
| while (iterator.moveNext()) { |
| computeDefaultValuesForDeclaration(iterator.current); |
| } |
| return count; |
| } |
| |
| /// Reports an error on generic function types used as bounds |
| /// |
| /// The function recursively searches for all generic function types in |
| /// [typeVariable.bound] and checks the bounds of type variables of the found |
| /// types for being generic function types. Additionally, the function checks |
| /// [typeVariable.bound] for being a generic function type. Returns `true` if |
| /// any errors were reported. |
| bool _recursivelyReportGenericFunctionTypesAsBoundsForVariable( |
| NominalVariableBuilder typeVariable) { |
| if (libraryFeatures.genericMetadata.isEnabled) return false; |
| |
| bool hasReportedErrors = false; |
| hasReportedErrors = _reportGenericFunctionTypeAsBoundIfNeeded( |
| typeVariable.bound, |
| typeVariableName: typeVariable.name, |
| fileUri: typeVariable.fileUri, |
| charOffset: typeVariable.charOffset) || |
| hasReportedErrors; |
| hasReportedErrors = _recursivelyReportGenericFunctionTypesAsBoundsForType( |
| typeVariable.bound) || |
| hasReportedErrors; |
| return hasReportedErrors; |
| } |
| |
| /// Reports an error on generic function types used as bounds |
| /// |
| /// The function recursively searches for all generic function types in |
| /// [typeBuilder] and checks the bounds of type variables of the found types |
| /// for being generic function types. Returns `true` if any errors were |
| /// reported. |
| bool _recursivelyReportGenericFunctionTypesAsBoundsForType( |
| TypeBuilder? typeBuilder) { |
| if (libraryFeatures.genericMetadata.isEnabled) return false; |
| |
| List<FunctionTypeBuilder> genericFunctionTypeBuilders = |
| <FunctionTypeBuilder>[]; |
| findUnaliasedGenericFunctionTypes(typeBuilder, |
| result: genericFunctionTypeBuilders); |
| bool hasReportedErrors = false; |
| for (FunctionTypeBuilder genericFunctionTypeBuilder |
| in genericFunctionTypeBuilders) { |
| assert( |
| genericFunctionTypeBuilder.typeVariables != null, |
| "Function 'findUnaliasedGenericFunctionTypes' " |
| "returned a function type without type variables."); |
| for (StructuralVariableBuilder typeVariable |
| in genericFunctionTypeBuilder.typeVariables!) { |
| hasReportedErrors = _reportGenericFunctionTypeAsBoundIfNeeded( |
| typeVariable.bound, |
| typeVariableName: typeVariable.name, |
| fileUri: typeVariable.fileUri, |
| charOffset: typeVariable.charOffset) || |
| hasReportedErrors; |
| } |
| } |
| return hasReportedErrors; |
| } |
| |
| /// Reports an error if [bound] is a generic function type |
| /// |
| /// Returns `true` if any errors were reported. |
| bool _reportGenericFunctionTypeAsBoundIfNeeded(TypeBuilder? bound, |
| {required String typeVariableName, |
| Uri? fileUri, |
| required int charOffset}) { |
| if (libraryFeatures.genericMetadata.isEnabled) return false; |
| |
| bool isUnaliasedGenericFunctionType = bound is FunctionTypeBuilder && |
| bound.typeVariables != null && |
| bound.typeVariables!.isNotEmpty; |
| bool isAliasedGenericFunctionType = false; |
| TypeDeclarationBuilder? declaration = bound?.declaration; |
| // TODO(cstefantsova): Unalias beyond the first layer for the check. |
| if (declaration is TypeAliasBuilder) { |
| // Coverage-ignore-block(suite): Not run. |
| TypeBuilder? rhsType = declaration.type; |
| if (rhsType is FunctionTypeBuilder && |
| rhsType.typeVariables != null && |
| rhsType.typeVariables!.isNotEmpty) { |
| isAliasedGenericFunctionType = true; |
| } |
| } |
| |
| if (isUnaliasedGenericFunctionType || isAliasedGenericFunctionType) { |
| addProblem(messageGenericFunctionTypeInBound, charOffset, |
| typeVariableName.length, fileUri); |
| return true; |
| } |
| return false; |
| } |
| |
| @override |
| int computeVariances() { |
| int count = 0; |
| |
| Iterator<Builder> iterator = libraryBuilder.localMembersIterator; |
| while (iterator.moveNext()) { |
| Builder? declaration = iterator.current; |
| while (declaration != null) { |
| if (declaration is TypeAliasBuilder && |
| declaration.typeVariablesCount > 0) { |
| for (NominalVariableBuilder typeParameter |
| in declaration.typeVariables!) { |
| typeParameter.variance = declaration.type |
| .computeTypeVariableBuilderVariance(typeParameter, |
| sourceLoader: libraryBuilder.loader) |
| .variance!; |
| ++count; |
| } |
| } |
| declaration = declaration.next; |
| } |
| } |
| return count; |
| } |
| |
| @override |
| Message reportFeatureNotEnabled( |
| LibraryFeature feature, Uri fileUri, int charOffset, int length) { |
| assert(!feature.isEnabled); |
| Message message; |
| if (feature.isSupported) { |
| // TODO(johnniwinther): Ideally the error should actually be special-cased |
| // to mention that it is an experimental feature. |
| String enabledVersionText = feature.flag.isEnabledByDefault |
| ? feature.enabledVersion.toText() |
| : "the current release"; |
| if (_languageVersion.isExplicit) { |
| message = templateExperimentOptOutExplicit.withArguments( |
| feature.flag.name, enabledVersionText); |
| addProblem(message, charOffset, length, fileUri, |
| context: <LocatedMessage>[ |
| templateExperimentOptOutComment |
| .withArguments(feature.flag.name) |
| .withLocation(_languageVersion.fileUri!, |
| _languageVersion.charOffset, _languageVersion.charCount) |
| ]); |
| } else { |
| message = templateExperimentOptOutImplicit.withArguments( |
| feature.flag.name, enabledVersionText); |
| addProblem(message, charOffset, length, fileUri); |
| } |
| } else { |
| if (feature.flag.isEnabledByDefault) { |
| // Coverage-ignore-block(suite): Not run. |
| if (_languageVersion.version < feature.enabledVersion) { |
| message = |
| templateExperimentDisabledInvalidLanguageVersion.withArguments( |
| feature.flag.name, feature.enabledVersion.toText()); |
| addProblem(message, charOffset, length, fileUri); |
| } else { |
| message = templateExperimentDisabled.withArguments(feature.flag.name); |
| addProblem(message, charOffset, length, fileUri); |
| } |
| } else { |
| message = templateExperimentNotEnabledOffByDefault |
| .withArguments(feature.flag.name); |
| addProblem(message, charOffset, length, fileUri); |
| } |
| } |
| return message; |
| } |
| |
| @override |
| bool addPrefixFragment( |
| String name, PrefixFragment prefixFragment, int charOffset) { |
| Builder? existing = |
| libraryBuilder.prefixNameSpace.lookupLocalMember(name, setter: false); |
| existing ??= |
| libraryBuilder.libraryNameSpace.lookupLocalMember(name, setter: false); |
| if (existing is PrefixBuilder) { |
| assert(existing.next is! PrefixBuilder); |
| int? deferredFileOffset; |
| int? otherFileOffset; |
| if (prefixFragment.deferred) { |
| deferredFileOffset = prefixFragment.prefixOffset; |
| otherFileOffset = existing.charOffset; |
| } else if (existing.deferred) { |
| deferredFileOffset = existing.charOffset; |
| otherFileOffset = prefixFragment.prefixOffset; |
| } |
| if (deferredFileOffset != null) { |
| _problemReporting.addProblem( |
| templateDeferredPrefixDuplicated.withArguments(name), |
| deferredFileOffset, |
| noLength, |
| fileUri, |
| context: [ |
| templateDeferredPrefixDuplicatedCause |
| .withArguments(name) |
| .withLocation(fileUri, otherFileOffset!, noLength) |
| ]); |
| } |
| prefixFragment.builder = existing; |
| return false; |
| } else if (existing != null) { |
| String fullName = name; |
| _problemReporting.addProblem( |
| templateDuplicatedDeclaration.withArguments(fullName), |
| charOffset, |
| fullName.length, |
| prefixFragment.fileUri, |
| context: <LocatedMessage>[ |
| templateDuplicatedDeclarationCause |
| .withArguments(fullName) |
| .withLocation( |
| existing.fileUri!, existing.charOffset, fullName.length) |
| ]); |
| } |
| // TODO(johnniwinther): For enhanced parts, this should be the prefix name |
| // space for the compilation unit. |
| libraryBuilder.prefixNameSpace.addLocalMember( |
| name, prefixFragment.createPrefixBuilder(), |
| setter: false); |
| return true; |
| } |
| } |