blob: 5ecf697c63558c430393b659800b3640306c0a58 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
import 'package:front_end/src/builder/property_builder.dart';
import 'package:kernel/ast.dart' show Annotatable, Library, Version;
import 'package:kernel/reference_from_index.dart';
import '../api_prototype/experimental_flags.dart';
import '../base/combinator.dart' show CombinatorBuilder;
import '../base/export.dart' show Export;
import '../base/loader.dart' show Loader;
import '../base/lookup_result.dart';
import '../base/messages.dart'
show
FormattedMessage,
LocatedMessage,
Message,
ProblemReporting,
noLength,
templateDuplicatedExport,
templateInternalProblemConstructorNotFound,
templateInternalProblemNotFoundIn,
templateInternalProblemPrivateConstructorAccess;
import '../base/name_space.dart';
import '../base/problems.dart' show internalProblem;
import '../base/scope.dart';
import '../base/uri_offset.dart';
import '../fragment/fragment.dart';
import '../kernel/body_builder_context.dart';
import '../kernel/load_library_builder.dart';
import '../source/offset_map.dart';
import '../source/outline_builder.dart';
import '../source/source_class_builder.dart';
import '../source/source_library_builder.dart';
import '../source/source_loader.dart';
import '../source/type_parameter_scope_builder.dart';
import 'builder.dart';
import 'constructor_builder.dart';
import 'declaration_builders.dart';
import 'factory_builder.dart';
import 'member_builder.dart';
import 'metadata_builder.dart';
import 'name_iterator.dart';
import 'prefix_builder.dart';
import 'type_builder.dart';
sealed class CompilationUnit {
/// Returns the import uri for the compilation unit.
///
/// This is the canonical uri for the compilation unit, for instance
/// 'dart:core'.
Uri get importUri;
Uri get fileUri;
bool get isSynthetic;
/// If true, the library is not supported through the 'dart.library.*' value
/// used in conditional imports and `bool.fromEnvironment` constants.
bool get isUnsupported;
Loader get loader;
/// The [LibraryBuilder] for the library that this compilation unit belongs
/// to.
///
/// This is only valid after `SourceLoader.resolveParts` has be called.
LibraryBuilder get libraryBuilder;
bool get isPart;
bool get isAugmenting;
LibraryBuilder? get partOfLibrary;
/// Returns the [Uri]s for the libraries that this library depend upon, either
/// through import or export.
Iterable<Uri> get dependencies;
void recordAccess(
CompilationUnit accessor, int charOffset, int length, Uri fileUri);
List<Export> get exporters;
void addExporter(CompilationUnit exporter,
List<CombinatorBuilder>? combinators, int charOffset);
/// Add a problem with a severity determined by the severity of the message.
///
/// If [fileUri] is null, it defaults to `this.fileUri`.
///
/// See `Loader.addMessage` for an explanation of the
/// arguments passed to this method.
void addProblem(Message message, int charOffset, int length, Uri? fileUri,
{bool wasHandled = false,
List<LocatedMessage>? context,
Severity? severity,
bool problemOnLibrary = false});
}
abstract class DillCompilationUnit implements CompilationUnit {}
abstract class SourceCompilationUnit
implements CompilationUnit, LibraryFragment {
OutlineBuilder createOutlineBuilder();
/// Creates a [SourceLibraryBuilder] for with this [SourceCompilationUnit] as
/// the main compilation unit.
SourceLibraryBuilder createLibrary([Library? library]);
@override
SourceLoader get loader;
OffsetMap get offsetMap;
/// The language version of this compilation unit 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
/// compilation unit if the compilation unit does not contain an explicit
/// `@dart=` annotation.
LanguageVersion get packageLanguageVersion;
/// 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.
void registerExplicitLanguageVersion(Version version,
{int offset = 0, int length = noLength});
// TODO(johnniwinther): Remove this.
bool get forAugmentationLibrary;
// TODO(johnniwinther): Remove this.
bool get forPatchLibrary;
/// If this is an compilation unit for an augmentation library, returns the
/// import uri for the origin library. Otherwise the [importUri] for the
/// compilation unit itself.
Uri get originImportUri;
@override
SourceLibraryBuilder get libraryBuilder;
/// The parent compilation unit.
///
/// This is the compilation unit that included this compilation unit as a
/// part or `null` if this is the root compilation unit of a library.
SourceCompilationUnit? get parentCompilationUnit;
LibraryFeatures get libraryFeatures;
/// Returns `true` if the compilation unit is part of a `dart:` library.
bool get isDartLibrary;
LanguageVersion get languageVersion;
String? get name;
int finishNativeMethods();
String? get partOfName;
Uri? get partOfUri;
List<MetadataBuilder>? get metadata;
/// The scope of this compilation unit.
///
/// This is the enclosing scope for all declarations within the compilation
/// unit.
LookupScope get compilationUnitScope;
/// The prefix scope of this compilation unit.
///
/// This contains all imports with prefixes declared in this compilation unit.
LookupScope get prefixScope;
NameSpace get prefixNameSpace;
bool get mayImplementRestrictedTypes;
void takeMixinApplications(
Map<SourceClassBuilder, TypeBuilder> mixinApplications);
void addDependencies(Library library, Set<SourceCompilationUnit> seen);
/// Runs through all part directives in this compilation unit and adds the
/// compilation unit for the parts to the [libraryBuilder] by adding them
/// to [includedParts]
///
/// [usedParts] is used to ensure that a compilation unit is only included in
/// one library. If the compilation unit is part of two libraries, it is only
/// included in the first and reported as an error on the second.
///
/// This should only be called on the main compilation unit for
/// [libraryBuilder]. Inclusion of nested parts is from within this method,
/// using [becomePart] for each individual subpart.
void includeParts(
List<SourceCompilationUnit> includedParts, Set<Uri> usedParts);
/// Includes this compilation unit as a part of [libraryBuilder] with
/// [parentCompilationUnit] as the parent compilation unit.
///
/// The parent compilation unit is used to define the compilation unit
/// scope of this compilation unit.
///
/// All fragment in this compilation unit will be added to
/// [libraryNameSpaceBuilder].
///
/// If parts with parts is supported (through the enhanced parts feature),
/// the compilation units of the part directives in this compilation unit
/// will be added [libraryBuilder] recursively.
void becomePart(
SourceLibraryBuilder libraryBuilder,
LibraryNameSpaceBuilder libraryNameSpaceBuilder,
SourceCompilationUnit parentCompilationUnit,
List<SourceCompilationUnit> includedParts,
Set<Uri> usedParts,
{required bool allowPartInParts});
void buildOutlineExpressions(
{required Annotatable annotatable,
required Uri annotatableFileUri,
required BodyBuilderContext bodyBuilderContext});
/// Reports that [feature] is not enabled, using [charOffset] and
/// [length] for the location of the message.
///
/// Return the primary message.
Message reportFeatureNotEnabled(
LibraryFeature feature, Uri fileUri, int charOffset, int length);
/// Registers that [augmentation] is a part of the library for which this is
/// the main compilation unit.
void registerAugmentation(CompilationUnit augmentation);
/// Reports [message] on all compilation units that access this compilation
/// unit.
void addProblemAtAccessors(Message message);
Iterable<LibraryAccess> get accessors;
/// Non-null if this library causes an error upon access, that is, there was
/// an error reading its source.
abstract Message? accessProblem;
/// Add a problem that might not be reported immediately.
///
/// Problems will be issued after source information has been added.
/// Once the problems has been issued, adding a new "postponed" problem will
/// be issued immediately.
void addPostponedProblem(
Message message, int charOffset, int length, Uri fileUri);
void issuePostponedProblems();
void markLanguageVersionFinal();
/// Index of the library we use references for.
IndexedLibrary? get indexedLibrary;
void addSyntheticImport(
{required String uri,
required String? prefix,
required List<CombinatorBuilder>? combinators,
required bool deferred});
void addImportedBuilderToScope(
{required String name,
required Builder builder,
required int charOffset});
void addImportsToScope();
void buildOutlineNode(Library library);
int finishDeferredLoadTearOffs(Library library);
/// This method instantiates type parameters to their bounds in some cases
/// where they were omitted by the programmer and not provided by the type
/// inference. The method returns the number of distinct type parameters
/// that were instantiated in this library.
int computeDefaultTypes(TypeBuilder dynamicType, TypeBuilder nullType,
TypeBuilder bottomType, ClassBuilder objectClass);
/// Computes variances of type parameters on typedefs.
///
/// The variance property of type parameters on typedefs is computed from the
/// use of the parameters in the right-hand side of the typedef definition.
int computeVariances();
/// Adds all unbound nominal parameters to [nominalParameters] and unbound
/// structural parameters to [structuralParameters], mapping them to
/// [libraryBuilder].
///
/// This is used to compute the bounds of type parameters while taking the
/// bound dependencies, which might span multiple libraries, into account.
void collectUnboundTypeParameters(
SourceLibraryBuilder libraryBuilder,
Map<NominalParameterBuilder, SourceLibraryBuilder> nominalParameters,
Map<StructuralParameterBuilder, SourceLibraryBuilder>
structuralParameters);
/// Adds [prefixFragment] to library name space.
///
/// Returns `true` if the prefix name was new to the name space. Otherwise the
/// prefix was merged with an existing prefix of the same name.
// TODO(johnniwinther): Remove this.
bool addPrefixFragment(
String name, PrefixFragment prefixFragment, int charOffset);
int resolveTypes(ProblemReporting problemReporting);
}
abstract class LibraryBuilder implements Builder, ProblemReporting {
NameSpace get libraryNameSpace;
NameSpace get exportNameSpace;
List<Export> get exporters;
LibraryBuilder? get partOfLibrary;
LibraryBuilder get nameOriginBuilder;
bool get mayImplementRestrictedTypes;
bool get isPart;
Loader get loader;
/// Returns the [Library] built by this builder.
Library get library;
@override
Uri get fileUri;
/// Returns the [Uri]s for the libraries that this library depend upon, either
/// through import or export.
Iterable<Uri> get dependencies;
/// Returns the import uri for the library.
///
/// This is the canonical uri for the library, for instance 'dart:core'.
Uri get importUri;
/// Returns the language [Version] used for this library.
Version get languageVersion;
/// If true, the library is not supported through the 'dart.library.*' value
/// used in conditional imports and `bool.fromEnvironment` constants.
bool get isUnsupported;
/// Returns an iterator of all members (typedefs, classes and members)
/// declared in this library, including duplicate declarations.
// TODO(johnniwinther): Should the only exist on [SourceLibraryBuilder]?
Iterator<Builder> get localMembersIterator;
/// Returns an iterator of all members of specified type
/// declared in this library, including duplicate declarations.
// TODO(johnniwinther): Should the only exist on [SourceLibraryBuilder]?
Iterator<T> localMembersIteratorOfType<T extends Builder>();
/// Returns an iterator of all members (typedefs, classes and members)
/// declared in this library, including duplicate declarations.
///
/// Compared to [localMembersIterator] this also gives access to the name
/// that the builders are mapped to.
NameIterator<Builder> get localMembersNameIterator;
/// [Iterator] for all declarations declared in this library or any of its
/// augmentations.
///
/// Duplicates and augmenting members are _not_ included.
Iterator<T> fullMemberIterator<T extends Builder>();
/// [NameIterator] for all declarations declared in this class or any of its
/// augmentations.
///
/// Duplicates and augmenting members are _not_ included.
NameIterator<T> fullMemberNameIterator<T extends Builder>();
/// Returns true if the export scope was modified.
bool addToExportScope(String name, Builder member,
{required UriOffset uriOffset});
/// Looks up [constructorName] in the class named [className].
///
/// The class is looked up in this library's export scope unless
/// [bypassLibraryPrivacy] is true, in which case it is looked up in the
/// library scope of this library.
///
/// It is an error if no such class is found, or if the class doesn't have a
/// matching constructor (or factory).
///
/// If [constructorName] is null or the empty string, it's assumed to be an
/// unnamed constructor. it's an error if [constructorName] starts with
/// `"_"`, and [bypassLibraryPrivacy] is false.
MemberBuilder getConstructor(String className,
{String constructorName, bool bypassLibraryPrivacy = false});
void becomeCoreLibrary();
/// Lookups the member [name] declared in this library.
///
/// If [required] is `true` and no member is found an internal problem is
/// reported.
Builder? lookupLocalMember(String name, {bool required = false});
void recordAccess(
CompilationUnit accessor, int charOffset, int length, Uri fileUri);
/// Returns `true` if [typeDeclarationBuilder] is the 'Function' class defined
/// in [coreLibrary].
static bool isFunction(TypeDeclarationBuilder? typeDeclarationBuilder,
LibraryBuilder coreLibrary) {
return typeDeclarationBuilder is ClassBuilder &&
typeDeclarationBuilder.name == 'Function' &&
typeDeclarationBuilder.libraryBuilder == coreLibrary;
}
/// Returns `true` if [typeDeclarationBuilder] is the 'Record' class defined
/// in [coreLibrary].
static bool isRecord(TypeDeclarationBuilder? typeDeclarationBuilder,
LibraryBuilder coreLibrary) {
return typeDeclarationBuilder is ClassBuilder &&
typeDeclarationBuilder.name == 'Record' &&
typeDeclarationBuilder.libraryBuilder == coreLibrary;
}
}
abstract class LibraryBuilderImpl extends BuilderImpl
implements LibraryBuilder {
@override
final Uri fileUri;
LibraryBuilderImpl(this.fileUri);
@override
// Coverage-ignore(suite): Not run.
bool get isSynthetic => false;
@override
// Coverage-ignore(suite): Not run.
Builder? get parent => null;
@override
int get fileOffset => -1;
@override
bool get isPart => false;
@override
Loader get loader;
@override
Uri get importUri;
@override
Iterator<Builder> get localMembersIterator {
return libraryNameSpace.filteredIterator(includeDuplicates: true);
}
@override
Iterator<T> localMembersIteratorOfType<T extends Builder>() {
return libraryNameSpace.filteredIterator<T>(includeDuplicates: true);
}
@override
NameIterator<Builder> get localMembersNameIterator {
return libraryNameSpace.filteredNameIterator(includeDuplicates: true);
}
@override
FormattedMessage? addProblem(
Message message, int charOffset, int length, Uri? fileUri,
{bool wasHandled = false,
List<LocatedMessage>? context,
Severity? severity,
bool problemOnLibrary = false}) {
fileUri ??= this.fileUri;
return loader.addProblem(message, charOffset, length, fileUri,
wasHandled: wasHandled,
context: context,
severity: severity,
problemOnLibrary: true);
}
/// Computes a builder for the export collision between [declaration] and
/// [other]. If [declaration] is declared in [libraryNameSpace] then this is
/// returned instead of reporting a collision.
Builder _computeAmbiguousDeclarationForExport(
String name, Builder declaration, Builder other,
{required UriOffset uriOffset}) {
// Prefix builders and load library builders are not part of an export
// scope.
assert(declaration is! PrefixBuilder,
"Unexpected prefix builder $declaration.");
assert(other is! PrefixBuilder, "Unexpected prefix builder $other.");
assert(declaration is! LoadLibraryBuilder,
"Unexpected load library builder $declaration.");
assert(other is! LoadLibraryBuilder,
"Unexpected load library builder $other.");
if (declaration == other) return declaration;
if (declaration is InvalidTypeDeclarationBuilder) return declaration;
if (other is InvalidTypeDeclarationBuilder) return other;
Builder? preferred;
Uri? uri;
Uri? otherUri;
if (libraryNameSpace.lookupLocalMember(name)?.getable == declaration) {
return declaration;
} else {
uri = computeLibraryUri(declaration);
otherUri = computeLibraryUri(other);
if (otherUri.isScheme("dart") && !uri.isScheme("dart")) {
preferred = declaration;
} else if (uri.isScheme("dart") && !otherUri.isScheme("dart")) {
preferred = other;
}
}
if (preferred != null) {
return preferred;
}
Uri firstUri = uri;
Uri secondUri = otherUri;
if (firstUri.toString().compareTo(secondUri.toString()) > 0) {
firstUri = secondUri;
secondUri = uri;
}
// TODO(ahe): We should probably use a context object here
// instead of including URIs in this message.
Message message =
templateDuplicatedExport.withArguments(name, firstUri, secondUri);
addProblem(message, uriOffset.fileOffset, noLength, uriOffset.uri);
// We report the error lazily (setting suppressMessage to false) because the
// spec 18.1 states that 'It is not an error if N is introduced by two or
// more imports but never referred to.'
return new InvalidTypeDeclarationBuilder(name,
message.withLocation(uriOffset.uri, uriOffset.fileOffset, name.length),
suppressMessage: false);
}
@override
bool addToExportScope(String name, Builder member,
{required UriOffset uriOffset}) {
if (name.startsWith("_")) return false;
if (member is PrefixBuilder) return false;
bool isSetter = isMappedAsSetter(member);
LookupResult? result = exportNameSpace.lookupLocalMember(name);
Builder? existing = isSetter ? result?.setable : result?.getable;
if (existing == member) {
return false;
} else {
if (member is MemberBuilder && member.isConflictingSetter) {
// TODO(johnniwinther): Remove this case when getables and setables are
// contained in the same map in the name space.
exportNameSpace.addLocalMember(name, member, setter: isSetter);
return true;
} else if (existing != null) {
exportNameSpace.lookupLocalMember(name);
Builder result = _computeAmbiguousDeclarationForExport(
name, existing, member,
uriOffset: uriOffset);
exportNameSpace.addLocalMember(name, result, setter: isSetter);
return result != existing;
} else {
exportNameSpace.addLocalMember(name, member, setter: isSetter);
return true;
}
}
}
@override
MemberBuilder getConstructor(String className,
{String? constructorName, bool bypassLibraryPrivacy = false}) {
constructorName ??= "";
if (constructorName.startsWith("_") && !bypassLibraryPrivacy) {
return internalProblem(
templateInternalProblemPrivateConstructorAccess
.withArguments(constructorName),
-1,
null);
}
Builder? cls = (bypassLibraryPrivacy ? libraryNameSpace : exportNameSpace)
.lookupLocalMember(className)
?.getable;
if (cls is TypeAliasBuilder) {
// Coverage-ignore-block(suite): Not run.
TypeAliasBuilder aliasBuilder = cls;
// No type arguments are available, but this method is only called in
// order to find constructors of specific non-generic classes (errors),
// so we can pass the empty list.
cls = aliasBuilder.unaliasDeclaration(const <TypeBuilder>[]);
}
if (cls is ClassBuilder) {
// TODO(ahe): This code is similar to code in `endNewExpression` in
// `body_builder.dart`, try to share it.
MemberBuilder? constructor =
cls.findConstructorOrFactory(constructorName, -1, fileUri, this);
if (constructor == null) {
// Fall-through to internal error below.
} else if (constructor is ConstructorBuilder) {
if (!cls.isAbstract) {
return constructor;
}
}
// Coverage-ignore(suite): Not run.
else if (constructor is FactoryBuilder) {
return constructor;
}
}
// Coverage-ignore-block(suite): Not run.
throw internalProblem(
templateInternalProblemConstructorNotFound.withArguments(
"$className.$constructorName", importUri),
-1,
null);
}
@override
Builder? lookupLocalMember(String name, {bool required = false}) {
Builder? builder = libraryNameSpace.lookupLocalMember(name)?.getable;
if (required && builder == null) {
internalProblem(
templateInternalProblemNotFoundIn.withArguments(
name, fullNameForErrors),
-1,
null);
}
return builder;
}
@override
void recordAccess(
CompilationUnit accessor, int charOffset, int length, Uri fileUri) {}
@override
String toString() {
return '$runtimeType(${isPart ? fileUri : importUri})';
}
}