b9348c7216
Change-Id: I50a965b8061572af8a5e408b037648b1f7ff2e0b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/413201 Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
1197 lines
42 KiB
Dart
1197 lines
42 KiB
Dart
// 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;
|
|
|
|
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;
|
|
|
|
late final BuilderFactoryImpl _builderFactory;
|
|
|
|
late final BuilderFactoryResult _builderFactoryResult;
|
|
|
|
final LibraryNameSpaceBuilder _libraryNameSpaceBuilder;
|
|
|
|
final NameSpace _importNameSpace;
|
|
|
|
late final LookupScope _importScope;
|
|
|
|
final NameSpace _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;
|
|
|
|
@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? 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();
|
|
NameSpace prefixNameSpace = new NameSpaceImpl();
|
|
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,
|
|
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,
|
|
LookupScope? parentScope,
|
|
required NameSpace importNameSpace,
|
|
required NameSpace prefixNameSpace,
|
|
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,
|
|
_prefixNameSpace = prefixNameSpace,
|
|
_augmentationRoot = augmentationRoot,
|
|
_nameOrigin = nameOrigin,
|
|
_parentScope = parentScope,
|
|
_referenceIsPartOwner = referenceIsPartOwner,
|
|
_problemReporting = new LibraryProblemReporting(loader, fileUri) {
|
|
LookupScope scope =
|
|
_importScope = new CompilationUnitImportScope(this, _importNameSpace);
|
|
_prefixScope = new CompilationUnitPrefixScope(
|
|
prefixNameSpace, ScopeKind.prefix, 'prefix',
|
|
parent: scope);
|
|
_compilationUnitScope = new CompilationUnitScope(
|
|
this, ScopeKind.compilationUnit, 'compilation-unit',
|
|
parent: _prefixScope);
|
|
|
|
// TODO(johnniwinther): Create these in [createOutlineBuilder].
|
|
_builderFactoryResult = _builderFactory = new BuilderFactoryImpl(
|
|
compilationUnit: this,
|
|
augmentationRoot: augmentationRoot ?? this,
|
|
libraryNameSpaceBuilder: libraryNameSpaceBuilder,
|
|
problemReporting: _problemReporting,
|
|
scope: _compilationUnitScope,
|
|
indexedLibrary: indexedLibrary);
|
|
}
|
|
|
|
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(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) {
|
|
// If trying to set a language version that is higher than the current sdk
|
|
// version it's an error.
|
|
addPostponedProblem(
|
|
templateLanguageVersionTooHighExplicit.withArguments(
|
|
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(
|
|
templateLanguageVersionTooLowExplicit.withArguments(
|
|
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 _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(
|
|
_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,
|
|
origin: _augmentationRoot
|
|
// Coverage-ignore(suite): Not run.
|
|
?.libraryBuilder,
|
|
target: library,
|
|
indexedLibrary: indexedLibrary,
|
|
referenceIsPartOwner: _referenceIsPartOwner,
|
|
isUnsupported: isUnsupported,
|
|
isAugmentation: forAugmentationLibrary,
|
|
isPatch: forPatchLibrary,
|
|
parentScope: _parentScope,
|
|
importNameSpace: _importNameSpace,
|
|
libraryNameSpaceBuilder: _libraryNameSpaceBuilder);
|
|
_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.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 _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: toCombinators(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: toCombinators(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 compilationUnitScope => _compilationUnitScope;
|
|
|
|
// Coverage-ignore(suite): Not run.
|
|
LookupScope get importScope => _importScope;
|
|
|
|
@override
|
|
LookupScope get prefixScope => _prefixScope;
|
|
|
|
@override
|
|
NameSpace get prefixNameSpace => _prefixNameSpace;
|
|
|
|
@override
|
|
void takeMixinApplications(
|
|
Map<SourceClassBuilder, TypeBuilder> mixinApplications) {
|
|
_builderFactoryResult.takeMixinApplications(mixinApplications);
|
|
}
|
|
|
|
@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 _builderFactoryResult.parts) {
|
|
// TODO(johnniwinther): Use [part.offset] in messages.
|
|
if (part.compilationUnit == this) {
|
|
addProblem(messagePartOfSelf, -1, noLength, fileUri);
|
|
} 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)
|
|
]);
|
|
} 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(
|
|
templatePartTwice.withArguments(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(
|
|
templatePartOfUriMismatch.withArguments(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(
|
|
templatePartOfLibraryNameMismatch.withArguments(part.fileUri,
|
|
parentCompilationUnit.name!, part.partOfName!),
|
|
partOffset,
|
|
noLength,
|
|
parentCompilationUnit.fileUri);
|
|
return;
|
|
}
|
|
} else {
|
|
parentCompilationUnit.addProblem(
|
|
templatePartOfUseUri.withArguments(part.fileUri,
|
|
parentCompilationUnit.fileUri, part.partOfName!),
|
|
partOffset,
|
|
noLength,
|
|
parentCompilationUnit.fileUri);
|
|
return;
|
|
}
|
|
} else {
|
|
assert(!part.isPart);
|
|
if (isNotMalformedUriScheme(part.fileUri)) {
|
|
parentCompilationUnit.addProblem(
|
|
templateMissingPartOf.withArguments(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(
|
|
templateMissingPartOf.withArguments(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 &&
|
|
// 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.isPatch) {
|
|
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(messageLanguageVersionPatchContext.withLocation(
|
|
part.languageVersion.fileUri!,
|
|
part.languageVersion.charOffset,
|
|
part.languageVersion.charCount));
|
|
}
|
|
parentCompilationUnit.addProblem(messageLanguageVersionMismatchInPatch,
|
|
partOffset, noLength, partUri,
|
|
context: context);
|
|
} else {
|
|
if (part.languageVersion.isExplicit) {
|
|
context.add(messageLanguageVersionPartContext.withLocation(
|
|
part.languageVersion.fileUri!,
|
|
part.languageVersion.charOffset,
|
|
part.languageVersion.charCount));
|
|
}
|
|
parentCompilationUnit.addProblem(
|
|
messageLanguageVersionMismatchInPart, 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 _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 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 (_builderFactoryResult.parts.isNotEmpty) {
|
|
List<LocatedMessage> context = <LocatedMessage>[
|
|
messagePartInPartLibraryContext.withLocation(
|
|
libraryBuilder.fileUri, -1, 1),
|
|
];
|
|
for (Part part in _builderFactoryResult.parts) {
|
|
addProblem(messagePartInPart, 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(
|
|
Annotatable annotatable, BodyBuilderContext bodyBuilderContext,
|
|
{required bool createFileUriExpression}) {
|
|
MetadataBuilder.buildAnnotations(annotatable, metadata, bodyBuilderContext,
|
|
libraryBuilder, fileUri, compilationUnitScope,
|
|
createFileUriExpression: createFileUriExpression);
|
|
}
|
|
|
|
@override
|
|
void collectUnboundTypeParameters(
|
|
SourceLibraryBuilder libraryBuilder,
|
|
Map<NominalParameterBuilder, SourceLibraryBuilder> nominalVariables,
|
|
Map<StructuralParameterBuilder, SourceLibraryBuilder>
|
|
structuralVariables) {
|
|
_builderFactoryResult.collectUnboundTypeParameters(
|
|
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 (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?
|
|
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) {
|
|
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;
|
|
|
|
List<StructuralParameterBuilder> unboundTypeParameters = [];
|
|
ComputeDefaultTypeContext context = new ComputeDefaultTypeContext(
|
|
_problemReporting, libraryFeatures, unboundTypeParameters,
|
|
dynamicType: dynamicType, bottomType: bottomType);
|
|
|
|
Iterator<Builder> iterator = libraryBuilder.localMembersIterator;
|
|
while (iterator.moveNext()) {
|
|
Builder 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}).");
|
|
}
|
|
}
|
|
|
|
_builderFactoryResult
|
|
.registerUnresolvedStructuralParameters(unboundTypeParameters);
|
|
|
|
return count;
|
|
}
|
|
|
|
@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.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 = 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 = 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.fileOffset;
|
|
} else if (existing.deferred) {
|
|
deferredFileOffset = existing.fileOffset;
|
|
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.fileOffset, fullName.length)
|
|
]);
|
|
}
|
|
prefixNameSpace.addLocalMember(name, prefixFragment.createPrefixBuilder(),
|
|
setter: false);
|
|
return true;
|
|
}
|
|
}
|