blob: 9597440455ab87fade4be0df0cada82dd9889552 [file] [log] [blame]
// 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;
}