| // 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. |
| |
| import 'package:_fe_analyzer_shared/src/parser/class_member_parser.dart' |
| show ClassMemberParser; |
| import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token; |
| import 'package:kernel/ast.dart' hide Combinator, MapLiteralEntry; |
| import 'package:kernel/reference_from_index.dart' show IndexedLibrary; |
| |
| import '../api_prototype/experimental_flags.dart'; |
| import '../base/combinator.dart' show CombinatorBuilder; |
| import '../base/export.dart' show Export; |
| import '../base/import.dart' show Import; |
| import '../base/lookup_result.dart'; |
| import '../base/messages.dart'; |
| import '../base/name_space.dart'; |
| import '../base/scope.dart'; |
| import '../base/uri_offset.dart'; |
| import '../base/uris.dart'; |
| import '../builder/builder.dart'; |
| import '../builder/compilation_unit.dart'; |
| import '../builder/constructor_builder.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/dynamic_type_declaration_builder.dart'; |
| import '../builder/library_builder.dart'; |
| import '../builder/member_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/never_type_declaration_builder.dart'; |
| import '../builder/prefix_builder.dart'; |
| import '../builder/property_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../kernel/body_builder_context.dart'; |
| import '../kernel/type_algorithms.dart' show ComputeDefaultTypeContext; |
| import '../kernel/utils.dart' show toCombinators; |
| import 'fragment_factory.dart'; |
| import 'fragment_factory_impl.dart'; |
| import 'name_space_builder.dart'; |
| import 'native_method_registry.dart'; |
| import 'offset_map.dart'; |
| import 'outline_builder.dart'; |
| import 'source_declaration_builder.dart'; |
| import 'source_library_builder.dart'; |
| import 'source_loader.dart' show SourceLoader; |
| import 'source_member_builder.dart'; |
| import 'source_type_alias_builder.dart'; |
| import 'type_parameter_factory.dart'; |
| import 'type_scope.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; |
| |
| // TODO(johnniwinther): Can we avoid this? |
| final bool? _referenceIsPartOwner; |
| |
| // TODO(johnniwinther): Pass only the [Reference] instead. |
| final LibraryBuilder? _nameOrigin; |
| |
| final LookupScope? _parentScope; |
| |
| SourceCompilationUnit? _parentCompilationUnit; |
| |
| /// 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; |
| |
| final _CompilationUnitData _compilationUnitData = new _CompilationUnitData(); |
| |
| final NativeMethodRegistry _native = new NativeMethodRegistry(); |
| |
| final TypeParameterFactory _typeParameterFactory = new TypeParameterFactory(); |
| |
| final LibraryNameSpaceBuilder _libraryNameSpaceBuilder; |
| |
| final SourceCompilationUnit? _augmentationRoot; |
| |
| final ComputedMutableNameSpace _importNameSpace; |
| |
| late final LookupScope _importScope; |
| |
| final MutableNameSpace _prefixNameSpace; |
| |
| late final LookupScope _prefixScope; |
| |
| LibraryFeatures? _libraryFeatures; |
| |
| @override |
| final bool forAugmentationLibrary; |
| |
| @override |
| final bool forPatchLibrary; |
| |
| @override |
| final bool isAugmenting; |
| |
| @override |
| final bool isUnsupported; |
| |
| late final LookupScope _compilationUnitScope; |
| |
| late final TypeScope _typeScope; |
| |
| @override |
| final bool mayImplementRestrictedTypes; |
| |
| 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? resolveInLibrary, |
| required bool? referenceIsPartOwner, |
| required bool forPatchLibrary, |
| required bool isAugmenting, |
| required bool isUnsupported, |
| required SourceLoader loader, |
| required bool mayImplementRestrictedTypes, |
| }) { |
| LibraryNameSpaceBuilder libraryNameSpaceBuilder = |
| new LibraryNameSpaceBuilder(); |
| ComputedMutableNameSpace importNameSpace = new ComputedMutableNameSpace(); |
| ComputedMutableNameSpace prefixNameSpace = new ComputedMutableNameSpace(); |
| return new SourceCompilationUnitImpl._( |
| libraryNameSpaceBuilder, |
| importUri: importUri, |
| fileUri: fileUri, |
| packageUri: packageUri, |
| packageLanguageVersion: packageLanguageVersion, |
| originImportUri: originImportUri, |
| indexedLibrary: indexedLibrary, |
| parentScope: parentScope, |
| importNameSpace: importNameSpace, |
| prefixNameSpace: prefixNameSpace, |
| forAugmentationLibrary: forAugmentationLibrary, |
| augmentationRoot: augmentationRoot, |
| resolveInLibrary: resolveInLibrary, |
| 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, |
| LookupScope? parentScope, |
| required ComputedMutableNameSpace importNameSpace, |
| required ComputedMutableNameSpace prefixNameSpace, |
| required this.forAugmentationLibrary, |
| required SourceCompilationUnit? augmentationRoot, |
| required LibraryBuilder? resolveInLibrary, |
| 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, |
| _prefixNameSpace = prefixNameSpace, |
| _nameOrigin = resolveInLibrary, |
| _parentScope = parentScope, |
| _referenceIsPartOwner = referenceIsPartOwner, |
| _problemReporting = new LibraryProblemReporting(loader, fileUri), |
| _augmentationRoot = augmentationRoot { |
| LookupScope scope = _importScope = new CompilationUnitImportScope( |
| this, |
| _importNameSpace, |
| ); |
| _prefixScope = new CompilationUnitPrefixScope( |
| prefixNameSpace, |
| ScopeKind.prefix, |
| parent: scope, |
| ); |
| LookupScope libraryScope = _prefixScope; |
| if (resolveInLibrary != null) { |
| // Coverage-ignore-block(suite): Not run. |
| libraryScope = new NameSpaceLookupScope( |
| resolveInLibrary.libraryNameSpace, |
| ScopeKind.library, |
| parent: libraryScope, |
| ); |
| } |
| _compilationUnitScope = new CompilationUnitScope( |
| this, |
| ScopeKind.compilationUnit, |
| parent: libraryScope, |
| ); |
| _typeScope = new TypeScope(TypeScopeKind.library, _compilationUnitScope); |
| } |
| |
| 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"); |
| |
| @override |
| bool get isPatch => forPatchLibrary; |
| |
| /// 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!; |
| } |
| |
| List<CompilationUnit>? _augmentations; |
| |
| @override |
| void registerAugmentation(CompilationUnit augmentation) { |
| (_augmentations ??= []).add(augmentation); |
| } |
| |
| @override |
| SourceCompilationUnit? get parentCompilationUnit => _parentCompilationUnit; |
| |
| @override |
| void addExporter( |
| SourceCompilationUnit 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, |
| CfeSeverity? 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) { |
| // If trying to set a language version that is higher than the current sdk |
| // version it's an error. |
| addPostponedProblem( |
| codeLanguageVersionTooHighExplicit.withArgumentsOld( |
| version.major, |
| version.minor, |
| 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( |
| codeLanguageVersionTooLowExplicit.withArgumentsOld( |
| version.major, |
| version.minor, |
| 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 _compilationUnitData.exports) { |
| yield export.exportedCompilationUnit.importUri; |
| } |
| for (Import import in _compilationUnitData.imports) { |
| CompilationUnit? imported = import.importedCompilationUnit; |
| if (imported != null) { |
| yield imported.importUri; |
| } |
| } |
| } |
| |
| @override |
| bool get isPart => _compilationUnitData.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 |
| void buildOutline(Token tokens) { |
| assert(_offsetMap == null, "OffsetMap has already been set for $this"); |
| |
| // TODO(johnniwinther): Create these in [createOutlineBuilder]. |
| FragmentFactory fragmentFactory = new FragmentFactoryImpl( |
| compilationUnit: this, |
| augmentationRoot: _augmentationRoot ?? this, |
| libraryNameSpaceBuilder: _libraryNameSpaceBuilder, |
| problemReporting: _problemReporting, |
| scope: _compilationUnitScope, |
| indexedLibrary: indexedLibrary, |
| typeParameterFactory: _typeParameterFactory, |
| typeScope: _typeScope, |
| compilationUnitRegistry: _compilationUnitData, |
| nativeMethodRegistry: _native, |
| ); |
| |
| OutlineBuilder listener = new OutlineBuilder( |
| this, |
| fragmentFactory, |
| _offsetMap = new OffsetMap(fileUri), |
| ); |
| |
| new ClassMemberParser( |
| listener, |
| allowPatterns: libraryFeatures.patterns.isEnabled, |
| enableFeatureEnhancedParts: libraryFeatures.enhancedParts.isEnabled, |
| ).parseUnit(tokens); |
| } |
| |
| @override |
| SourceLibraryBuilder createLibrary([Library? library]) { |
| assert( |
| _languageVersion.isFinal, |
| "Can not create a SourceLibraryBuilder before the language version of " |
| "the compilation unit is finalized.", |
| ); |
| 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, |
| target: library, |
| indexedLibrary: indexedLibrary, |
| referenceIsPartOwner: _referenceIsPartOwner, |
| isUnsupported: isUnsupported, |
| isAugmentation: forAugmentationLibrary, |
| isPatch: forPatchLibrary, |
| parentScope: _parentScope, |
| importNameSpace: _importNameSpace, |
| libraryNameSpaceBuilder: _libraryNameSpaceBuilder, |
| ); |
| _problemReporting.registerLibrary(libraryBuilder.library); |
| if (isPart) { |
| // This is a part with no enclosing library. |
| addProblem(codePartOrphan, 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 is ConstructorBuilder) { |
| annotation = new ConstructorInvocation( |
| constructor.invokeTarget as Constructor, |
| arguments, |
| )..isConst = true; |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| annotation = new StaticInvocation( |
| constructor.invokeTarget 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 _compilationUnitData.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!; |
| Library targetLibrary = imported.library; |
| libraryDependency = new LibraryDependency.import( |
| targetLibrary, |
| name: import.prefix, |
| combinators: toCombinators(import.combinators), |
| )..fileOffset = import.importOffset; |
| } |
| library.addDependency(libraryDependency); |
| import.libraryDependency = libraryDependency; |
| } |
| for (Export export in _compilationUnitData.exports) { |
| LibraryDependency libraryDependency = new LibraryDependency.export( |
| export.exportedLibraryBuilder.library, |
| combinators: toCombinators(export.combinators), |
| )..fileOffset = export.charOffset; |
| library.addDependency(libraryDependency); |
| export.libraryDependency = libraryDependency; |
| } |
| } |
| |
| @override |
| String? get partOfName => _compilationUnitData.partOfName; |
| |
| @override |
| Uri? get partOfUri => _compilationUnitData.partOfUri; |
| |
| @override |
| LookupScope get compilationUnitScope => _compilationUnitScope; |
| |
| // Coverage-ignore(suite): Not run. |
| LookupScope get importScope => _importScope; |
| |
| @override |
| LookupScope get prefixScope => _prefixScope; |
| |
| @override |
| NameSpace get prefixNameSpace => _prefixNameSpace; |
| |
| @override |
| void includeParts( |
| List<SourceCompilationUnit> includedParts, |
| Set<Uri> usedParts, |
| ) { |
| _includeParts( |
| libraryBuilder: libraryBuilder, |
| libraryNameSpaceBuilder: _libraryNameSpaceBuilder, |
| includedParts: includedParts, |
| usedParts: usedParts, |
| ); |
| } |
| |
| void _includeParts({ |
| required SourceLibraryBuilder libraryBuilder, |
| required LibraryNameSpaceBuilder libraryNameSpaceBuilder, |
| required List<SourceCompilationUnit> includedParts, |
| required Set<Uri> usedParts, |
| }) { |
| Set<Uri> seenParts = new Set<Uri>(); |
| for (Part part in _compilationUnitData.parts) { |
| // TODO(johnniwinther): Use [part.offset] in messages. |
| if (part.compilationUnit == this) { |
| addProblem(codePartOfSelf, -1, noLength, fileUri); |
| } else if (seenParts.add(part.compilationUnit.fileUri)) { |
| if (part.compilationUnit.partOfLibrary != null) { |
| addProblem( |
| codePartOfTwoLibraries, |
| -1, |
| noLength, |
| part.compilationUnit.fileUri, |
| context: [ |
| codePartOfTwoLibrariesContext.withLocation( |
| part.compilationUnit.partOfLibrary!.fileUri, |
| -1, |
| noLength, |
| ), |
| codePartOfTwoLibrariesContext.withLocation(fileUri, -1, noLength), |
| ], |
| ); |
| } else { |
| usedParts.add(part.compilationUnit.importUri); |
| _includePartIfValid( |
| libraryBuilder: libraryBuilder, |
| libraryNameSpaceBuilder: libraryNameSpaceBuilder, |
| parentCompilationUnit: this, |
| includedParts: includedParts, |
| part: part.compilationUnit, |
| usedParts: usedParts, |
| partOffset: part.fileOffset, |
| partUri: fileUri, |
| ); |
| } |
| } else { |
| addProblem( |
| codePartTwice.withArgumentsOld(part.compilationUnit.fileUri), |
| -1, |
| noLength, |
| fileUri, |
| ); |
| } |
| } |
| if (_augmentations != null) { |
| for (CompilationUnit augmentation in _augmentations!) { |
| switch (augmentation) { |
| case SourceCompilationUnit(): |
| _includePart( |
| libraryBuilder, |
| libraryNameSpaceBuilder, |
| this, |
| includedParts, |
| augmentation, |
| usedParts, |
| partOffset: -1, |
| partUri: augmentation.fileUri, |
| allowPartInParts: true, |
| ); |
| // Coverage-ignore(suite): Not run. |
| case DillCompilationUnit(): |
| // TODO(johnniwinther): Report an error here. |
| throw new UnsupportedError("Unexpected augmentation $augmentation"); |
| } |
| } |
| } |
| } |
| |
| void _includePartIfValid({ |
| required SourceLibraryBuilder libraryBuilder, |
| required LibraryNameSpaceBuilder libraryNameSpaceBuilder, |
| required SourceCompilationUnit parentCompilationUnit, |
| required List<SourceCompilationUnit> includedParts, |
| required CompilationUnit part, |
| required Set<Uri> usedParts, |
| required Uri partUri, |
| required int partOffset, |
| }) { |
| switch (part) { |
| case SourceCompilationUnit(): |
| if (part.partOfUri != null) { |
| if (isNotMalformedUriScheme(part.partOfUri!) && |
| part.partOfUri != parentCompilationUnit.importUri) { |
| parentCompilationUnit.addProblem( |
| codePartOfUriMismatch.withArgumentsOld( |
| part.fileUri, |
| parentCompilationUnit.importUri, |
| part.partOfUri!, |
| ), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri, |
| ); |
| return; |
| } |
| } else if (part.partOfName != null) { |
| if (parentCompilationUnit.name != null) { |
| if (part.partOfName != parentCompilationUnit.name) { |
| parentCompilationUnit.addProblem( |
| codePartOfLibraryNameMismatch.withArgumentsOld( |
| part.fileUri, |
| parentCompilationUnit.name!, |
| part.partOfName!, |
| ), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri, |
| ); |
| return; |
| } |
| } else { |
| parentCompilationUnit.addProblem( |
| codePartOfUseUri.withArgumentsOld( |
| part.fileUri, |
| parentCompilationUnit.fileUri, |
| part.partOfName!, |
| ), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri, |
| ); |
| return; |
| } |
| } else { |
| assert(!part.isPart); |
| if (isNotMalformedUriScheme(part.fileUri)) { |
| parentCompilationUnit.addProblem( |
| codeMissingPartOf.withArgumentsOld(part.fileUri), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri, |
| ); |
| } |
| return; |
| } |
| _includePart( |
| libraryBuilder, |
| libraryNameSpaceBuilder, |
| parentCompilationUnit, |
| includedParts, |
| part, |
| usedParts, |
| partOffset: partOffset, |
| partUri: partUri, |
| allowPartInParts: |
| parentCompilationUnit.libraryFeatures.enhancedParts.isEnabled, |
| ); |
| 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( |
| codeMissingPartOf.withArgumentsOld(part.fileUri), |
| partOffset, |
| noLength, |
| parentCompilationUnit.fileUri, |
| ); |
| } |
| } |
| } |
| |
| void _includePart( |
| SourceLibraryBuilder libraryBuilder, |
| LibraryNameSpaceBuilder libraryNameSpaceBuilder, |
| SourceCompilationUnit parentCompilationUnit, |
| List<SourceCompilationUnit> includedParts, |
| SourceCompilationUnit part, |
| Set<Uri> usedParts, { |
| required int partOffset, |
| required Uri partUri, |
| required bool allowPartInParts, |
| }) { |
| // 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 && |
| parentCompilationUnit.languageVersion.valid && |
| part.languageVersion.valid) { |
| // 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( |
| codeLanguageVersionLibraryContext.withLocation( |
| parentCompilationUnit.languageVersion.fileUri!, |
| parentCompilationUnit.languageVersion.charOffset, |
| parentCompilationUnit.languageVersion.charCount, |
| ), |
| ); |
| } |
| |
| if (part.isPatch) { |
| // Coverage-ignore-block(suite): Not run. |
| if (part.languageVersion.isExplicit) { |
| // Patches are implicitly include, so if we have an explicit language |
| // version, then point to this instead of the top of the file. |
| partOffset = part.languageVersion.charOffset; |
| partUri = part.languageVersion.fileUri!; |
| context.add( |
| codeLanguageVersionPatchContext.withLocation( |
| part.languageVersion.fileUri!, |
| part.languageVersion.charOffset, |
| part.languageVersion.charCount, |
| ), |
| ); |
| } |
| parentCompilationUnit.addProblem( |
| codeLanguageVersionMismatchInPatch, |
| partOffset, |
| noLength, |
| partUri, |
| context: context, |
| ); |
| } else { |
| if (part.languageVersion.isExplicit) { |
| context.add( |
| codeLanguageVersionPartContext.withLocation( |
| part.languageVersion.fileUri!, |
| part.languageVersion.charOffset, |
| part.languageVersion.charCount, |
| ), |
| ); |
| } |
| parentCompilationUnit.addProblem( |
| codeLanguageVersionMismatchInPart, |
| partOffset, |
| noLength, |
| partUri, |
| context: context, |
| ); |
| } |
| } |
| |
| includedParts.add(part); |
| part.becomePart( |
| libraryBuilder, |
| libraryNameSpaceBuilder, |
| parentCompilationUnit, |
| includedParts, |
| usedParts, |
| allowPartInParts: allowPartInParts, |
| ); |
| } |
| |
| 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 _typeScope.resolveTypes(problemReporting); |
| } |
| |
| @override |
| int finishNativeMethods(SourceLoader loader) { |
| return _native.finishNativeMethods(loader); |
| } |
| |
| void _clearPartsAndReportExporters() { |
| assert(_libraryBuilder != null, "Library has not be set."); |
| _compilationUnitData.parts.clear(); |
| if (exporters.isNotEmpty) { |
| List<LocatedMessage> context = <LocatedMessage>[ |
| codePartExportContext.withLocation(fileUri, -1, 1), |
| ]; |
| for (Export export in exporters) { |
| export.exporter.addProblem( |
| codePartExport, |
| export.charOffset, |
| "export".length, |
| null, |
| context: context, |
| ); |
| } |
| } |
| } |
| |
| @override |
| void becomePart( |
| SourceLibraryBuilder libraryBuilder, |
| LibraryNameSpaceBuilder libraryNameSpaceBuilder, |
| SourceCompilationUnit parentCompilationUnit, |
| List<SourceCompilationUnit> includedParts, |
| Set<Uri> usedParts, { |
| required bool allowPartInParts, |
| }) { |
| assert( |
| _libraryBuilder == null, |
| "Compilation unit $this is already part of library $_libraryBuilder. " |
| "Trying to include it in $libraryBuilder.", |
| ); |
| _libraryBuilder = libraryBuilder; |
| _partOfLibrary = libraryBuilder; |
| _parentCompilationUnit = parentCompilationUnit; |
| if (!allowPartInParts) { |
| if (_compilationUnitData.parts.isNotEmpty) { |
| List<LocatedMessage> context = <LocatedMessage>[ |
| codePartInPartLibraryContext.withLocation( |
| libraryBuilder.fileUri, |
| -1, |
| 1, |
| ), |
| ]; |
| for (Part part in _compilationUnitData.parts) { |
| addProblem( |
| codePartInPart, |
| part.fileOffset, |
| 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); |
| } else { |
| _becomePart(libraryBuilder, libraryNameSpaceBuilder); |
| _includeParts( |
| libraryBuilder: libraryBuilder, |
| libraryNameSpaceBuilder: libraryNameSpaceBuilder, |
| includedParts: includedParts, |
| usedParts: usedParts, |
| ); |
| } |
| } |
| |
| @override |
| void buildOutlineExpressions({ |
| required Annotatable annotatable, |
| required Uri annotatableFileUri, |
| required BodyBuilderContext bodyBuilderContext, |
| }) { |
| MetadataBuilder.buildAnnotations( |
| annotatable: annotatable, |
| annotatableFileUri: annotatableFileUri, |
| metadata: metadata, |
| bodyBuilderContext: bodyBuilderContext, |
| libraryBuilder: libraryBuilder, |
| scope: compilationUnitScope, |
| ); |
| } |
| |
| @override |
| List<TypeParameterBuilder> collectUnboundTypeParameters() { |
| return _typeParameterFactory.collectTypeParameters(); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void addSyntheticImport({ |
| required Uri importUri, |
| required String? prefix, |
| required List<CombinatorBuilder>? combinators, |
| required bool deferred, |
| }) { |
| assert( |
| checkState(pending: [SourceCompilationUnitState.importsAddedToScope]), |
| ); |
| CompilationUnit? compilationUnit = loader.read( |
| importUri, |
| -1, |
| origin: null, |
| accessor: this, |
| isAugmentation: false, |
| referencesFromIndex: indexedLibrary, |
| ); |
| Import import = new Import( |
| this, |
| compilationUnit, |
| false, |
| deferred, |
| prefix, |
| combinators, |
| null, |
| fileUri, |
| -1, |
| -1, |
| nativeImportPath: null, |
| ); |
| _compilationUnitData.registerImport(import); |
| } |
| |
| @override |
| void addImportsToScope() { |
| assert(checkState(required: [SourceCompilationUnitState.initial])); |
| |
| bool hasCoreImport = |
| originImportUri == dartCore && |
| // Coverage-ignore(suite): Not run. |
| !forPatchLibrary; |
| for (Import import in _compilationUnitData.imports) { |
| if (import.importedCompilationUnit?.isPart ?? false) { |
| addProblem( |
| codePartOfInLibrary.withArgumentsOld( |
| import.importedCompilationUnit!.fileUri, |
| ), |
| import.importOffset, |
| noLength, |
| fileUri, |
| ); |
| } |
| if (import.importedLibraryBuilder == loader.coreLibrary) { |
| hasCoreImport = true; |
| } |
| import.finalizeImports(this); |
| } |
| if (parentCompilationUnit == null && !hasCoreImport) { |
| // 'dart:core' should only be implicitly imported into the root |
| // compilation unit. Parts without imports will have access to 'dart:core' |
| // from the parent compilation unit. |
| |
| // TODO(johnniwinther): Can we create the core import as a parent scope |
| // instead of copying it everywhere? |
| Iterator<NamedBuilder> iterator = loader.coreLibrary.exportNameSpace |
| .filteredIterator(); |
| while (iterator.moveNext()) { |
| NamedBuilder builder = iterator.current; |
| addImportedBuilderToScope( |
| name: builder.name, |
| builder: builder, |
| charOffset: -1, |
| ); |
| } |
| } |
| |
| state = SourceCompilationUnitState.importsAddedToScope; |
| } |
| |
| @override |
| void addImportedBuilderToScope({ |
| required String name, |
| required NamedBuilder builder, |
| required int charOffset, |
| }) { |
| bool isSetter = isMappedAsSetter(builder); |
| LookupResult? result = _importNameSpace.lookup(name); |
| |
| NamedBuilder? existing = isSetter ? result?.setable : result?.getable; |
| if (existing != null) { |
| if (existing != builder) { |
| _importNameSpace.replaceLocalMember( |
| name, |
| computeAmbiguousDeclarationForImport( |
| _problemReporting, |
| name, |
| existing, |
| builder, |
| uriOffset: new UriOffset(fileUri, charOffset), |
| ), |
| setter: isSetter, |
| ); |
| } |
| } else { |
| _importNameSpace.addLocalMember(name, builder, setter: isSetter); |
| } |
| if (builder is ExtensionBuilder) { |
| _importNameSpace.addExtension(builder); |
| } |
| } |
| |
| @override |
| void buildOutlineNode(Library library) { |
| for (LibraryPart libraryPart in _compilationUnitData.libraryParts) { |
| library.addPart(libraryPart); |
| } |
| } |
| |
| @override |
| int finishDeferredLoadTearOffs(Library library) { |
| assert( |
| checkState(required: [SourceCompilationUnitState.importsAddedToScope]), |
| ); |
| |
| int total = 0; |
| for (Import import in _compilationUnitData.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 => _compilationUnitData.metadata; |
| |
| @override |
| String? get name => _compilationUnitData.name; |
| |
| @override |
| int computeDefaultTypes( |
| TypeBuilder dynamicType, |
| TypeBuilder nullType, |
| TypeBuilder bottomType, |
| ClassBuilder objectClass, |
| ) { |
| int count = 0; |
| |
| ComputeDefaultTypeContext context = new ComputeDefaultTypeContext( |
| _problemReporting, |
| libraryFeatures, |
| _typeParameterFactory, |
| dynamicType: dynamicType, |
| bottomType: bottomType, |
| ); |
| |
| Iterator<NamedBuilder> iterator = libraryBuilder.unfilteredMembersIterator; |
| while (iterator.moveNext()) { |
| NamedBuilder declaration = iterator.current; |
| if (declaration is SourceDeclarationBuilder) { |
| count += declaration.computeDefaultTypes(context); |
| } else if (declaration is SourceTypeAliasBuilder) { |
| count += declaration.computeDefaultType(context); |
| } else if (declaration is SourceMemberBuilder) { |
| count += declaration.computeDefaultTypes( |
| context, |
| inErrorRecovery: false, |
| ); |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| assert( |
| declaration is PrefixBuilder || |
| declaration is DynamicTypeDeclarationBuilder || |
| declaration is NeverTypeDeclarationBuilder, |
| "Unexpected top level member $declaration " |
| "(${declaration.runtimeType}).", |
| ); |
| } |
| } |
| |
| return count; |
| } |
| |
| @override |
| int computeVariances() { |
| int count = 0; |
| |
| Iterator<NamedBuilder> iterator = libraryBuilder.unfilteredMembersIterator; |
| while (iterator.moveNext()) { |
| NamedBuilder? declaration = iterator.current; |
| while (declaration != null) { |
| if (declaration is TypeAliasBuilder && |
| declaration.typeParametersCount > 0) { |
| for (NominalParameterBuilder typeParameter |
| in declaration.typeParameters!) { |
| typeParameter.variance = declaration.type |
| .computeTypeParameterBuilderVariance( |
| 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 = codeExperimentOptOutExplicit.withArgumentsOld( |
| feature.flag.name, |
| enabledVersionText, |
| ); |
| addProblem( |
| message, |
| charOffset, |
| length, |
| fileUri, |
| context: <LocatedMessage>[ |
| codeExperimentOptOutComment |
| .withArgumentsOld(feature.flag.name) |
| .withLocation( |
| _languageVersion.fileUri!, |
| _languageVersion.charOffset, |
| _languageVersion.charCount, |
| ), |
| ], |
| ); |
| } else { |
| message = codeExperimentOptOutImplicit.withArgumentsOld( |
| 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 = codeExperimentDisabledInvalidLanguageVersion |
| .withArgumentsOld( |
| feature.flag.name, |
| feature.enabledVersion.toText(), |
| ); |
| addProblem(message, charOffset, length, fileUri); |
| } else { |
| message = codeExperimentDisabled.withArgumentsOld(feature.flag.name); |
| addProblem(message, charOffset, length, fileUri); |
| } |
| } else { |
| message = codeExperimentNotEnabledOffByDefault.withArgumentsOld( |
| feature.flag.name, |
| ); |
| addProblem(message, charOffset, length, fileUri); |
| } |
| } |
| return message; |
| } |
| |
| @override |
| bool addPrefixFragment( |
| String name, |
| PrefixFragment prefixFragment, |
| int charOffset, |
| ) { |
| Builder? existing = prefixNameSpace.lookup(name)?.getable; |
| existing ??= libraryBuilder.libraryNameSpace.lookup(name)?.getable; |
| if (existing is PrefixBuilder) { |
| assert(existing.next is! PrefixBuilder); |
| int? deferredFileOffset; |
| int? otherFileOffset; |
| if (prefixFragment.deferred) { |
| deferredFileOffset = prefixFragment.prefixOffset; |
| otherFileOffset = existing.fileOffset; |
| } else if (existing.deferred) { |
| deferredFileOffset = existing.fileOffset; |
| otherFileOffset = prefixFragment.prefixOffset; |
| } |
| if (deferredFileOffset != null) { |
| _problemReporting.addProblem( |
| codeDeferredPrefixDuplicated.withArgumentsOld(name), |
| deferredFileOffset, |
| noLength, |
| fileUri, |
| context: [ |
| codeDeferredPrefixDuplicatedCause |
| .withArgumentsOld(name) |
| .withLocation(fileUri, otherFileOffset!, noLength), |
| ], |
| ); |
| } |
| prefixFragment.builder = existing; |
| return false; |
| } else if (existing != null) { |
| String fullName = name; |
| _problemReporting.addProblem( |
| codeDuplicatedDeclaration.withArgumentsOld(fullName), |
| charOffset, |
| fullName.length, |
| prefixFragment.fileUri, |
| context: <LocatedMessage>[ |
| codeDuplicatedDeclarationCause |
| .withArgumentsOld(fullName) |
| .withLocation( |
| existing.fileUri!, |
| existing.fileOffset, |
| fullName.length, |
| ), |
| ], |
| ); |
| } |
| _prefixNameSpace.addLocalMember( |
| name, |
| prefixFragment.createPrefixBuilder(), |
| setter: false, |
| ); |
| return true; |
| } |
| } |
| |
| class _CompilationUnitData implements CompilationUnitRegistry { |
| String? _name; |
| |
| Uri? _partOfUri; |
| |
| String? _partOfName; |
| |
| List<MetadataBuilder>? _metadata; |
| |
| /// The part directives in this compilation unit. |
| final List<Part> _parts = []; |
| |
| final List<LibraryPart> _libraryParts = []; |
| |
| final List<Import> _imports = <Import>[]; |
| |
| final List<Export> _exports = <Export>[]; |
| |
| @override |
| void registerLibraryDirective({ |
| required String? libraryName, |
| required List<MetadataBuilder>? metadata, |
| }) { |
| _name = libraryName; |
| _metadata = metadata; |
| } |
| |
| @override |
| void registerPartOf({required String? name, required Uri? resolvedUri}) { |
| _partOfName = name; |
| _partOfUri = resolvedUri; |
| } |
| |
| @override |
| void registerPart(Part part) { |
| _parts.add(part); |
| } |
| |
| @override |
| void registerLibraryPart(LibraryPart libraryPart) { |
| _libraryParts.add(libraryPart); |
| } |
| |
| @override |
| void registerImport(Import import) { |
| _imports.add(import); |
| } |
| |
| @override |
| void registerExport(Export export) { |
| _exports.add(export); |
| } |
| |
| String? get name => _name; |
| |
| List<MetadataBuilder>? get metadata => _metadata; |
| |
| bool get isPart => _partOfName != null || _partOfUri != null; |
| |
| String? get partOfName => _partOfName; |
| |
| Uri? get partOfUri => _partOfUri; |
| |
| List<Part> get parts => _parts; |
| |
| List<LibraryPart> get libraryParts => _libraryParts; |
| |
| List<Import> get imports => _imports; |
| |
| List<Export> get exports => _exports; |
| } |