blob: 046494faab4b28eb06f1980e96ecec2052902072 [file] [log] [blame]
// Copyright (c) 2019, 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/field_promotability.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart' as file_state;
import 'package:analyzer/src/dart/analysis/file_state.dart' hide DirectiveUri;
import 'package:analyzer/src/dart/analysis/unlinked_data.dart';
import 'package:analyzer/src/dart/ast/ast.dart' as ast;
import 'package:analyzer/src/dart/ast/mixin_super_invoked_names.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/field_name_non_promotability_info.dart'
as element_model;
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/summary2/combinator.dart';
import 'package:analyzer/src/summary2/constructor_initializer_resolver.dart';
import 'package:analyzer/src/summary2/default_value_resolver.dart';
import 'package:analyzer/src/summary2/element_builder.dart';
import 'package:analyzer/src/summary2/export.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/metadata_resolver.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/summary2/reference_resolver.dart';
import 'package:analyzer/src/summary2/types_builder.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';
class DefiningLinkingUnit extends LinkingUnit {
DefiningLinkingUnit({required super.node, required super.element});
}
class ImplicitEnumNodes {
final EnumFragmentImpl element;
final ast.NamedTypeImpl valuesTypeNode;
final ast.VariableDeclarationImpl valuesNode;
final FieldFragmentImpl valuesElement;
final Set<String> valuesNames;
ast.ListLiteralImpl valuesInitializer;
ImplicitEnumNodes({
required this.element,
required this.valuesTypeNode,
required this.valuesNode,
required this.valuesElement,
required this.valuesNames,
required this.valuesInitializer,
});
}
class LibraryBuilder {
final Linker linker;
final LibraryFileKind kind;
final Uri uri;
final Reference reference;
final LibraryElementImpl element;
final List<LinkingUnit> units;
final Map<EnumFragmentImpl, ImplicitEnumNodes> implicitEnumNodes =
Map.identity();
/// Top fragments, in the same order as in AST.
final Map<LibraryFragmentImpl, List<FragmentImpl>> _topFragments = {};
/// Key: a parent fragment, e.g. [ClassFragmentImpl].
/// Value: fragments of its direct children.
///
/// For example `class A { void foo() {} }` has `foo` as child of `A`.
final Map<FragmentImpl, List<FragmentImpl>> _parentChildFragments =
Map.identity();
/// Local declarations.
final Map<String, Reference> _declaredReferences = {};
/// The export scope of the library.
ExportScope exportScope = ExportScope();
/// The `export` directives that export this library.
final List<Export> exports = [];
/// The identifier of the reference used for unnamed fragments.
int _nextUnnamedId = 0;
/// The fields that were speculatively created as [FieldFragmentImpl],
/// but we want to clear [VariableFragmentImpl.constantInitializer] for it
/// if the class will not end up with a `const` constructor. We don't know
/// at the time when we create them, because of future augmentations.
final Set<FieldFragmentImpl> finalInstanceFields = Set.identity();
LibraryBuilder._({
required this.linker,
required this.kind,
required this.uri,
required this.reference,
required this.element,
required this.units,
});
void addExporters() {
for (var (fragmentIndex, fragment) in element.units.indexed) {
for (var (exportIndex, exportElement)
in fragment.libraryExports.indexed) {
var exportedLibrary = exportElement.exportedLibrary2;
if (exportedLibrary == null) {
continue;
}
var exportedUri = exportedLibrary.source.uri;
var exportedBuilder = linker.builders[exportedUri];
var combinators = exportElement.combinators.build();
var export = Export(
exporter: this,
location: ExportLocation(
fragmentIndex: fragmentIndex,
exportIndex: exportIndex,
),
combinators: combinators,
);
if (exportedBuilder != null) {
exportedBuilder.exports.add(export);
} else {
var exportedReferences = exportedLibrary.exportedReferences;
for (var exported in exportedReferences) {
var reference = exported.reference;
var name = reference.name;
if (reference.isSetter) {
export.addToExportScope('$name=', exported);
} else {
export.addToExportScope(name, exported);
}
}
}
}
}
}
void addFragmentChild(FragmentImpl parent, FragmentImpl child) {
(_parentChildFragments[parent] ??= []).add(child);
}
void addTopFragment(LibraryFragmentImpl parent, FragmentImpl fragment) {
fragment.enclosingElement3 = parent;
(_topFragments[parent] ??= []).add(fragment);
}
void buildClassSyntheticConstructors() {
for (var classFragment in element.topLevelElements) {
if (classFragment is! ClassFragmentImpl) continue;
if (classFragment.isMixinApplication) continue;
if (classFragment.constructors.isNotEmpty) continue;
var fragment = ConstructorFragmentImpl(name2: 'new', nameOffset: -1)
..isSynthetic = true;
fragment.typeName = classFragment.name2;
var classElement = classFragment.element;
classElement.constructors = [
ConstructorElementImpl(
name3: fragment.name2,
reference: classElement.reference
.getChild('@constructor')
.addChild('new'),
firstFragment: fragment,
),
];
classFragment.constructors = [fragment].toFixedList();
}
}
/// Build elements for declarations in the library units, add top-level
/// declarations to the local scope, for combining into export scopes.
void buildElements() {
_buildDirectives(
kind: kind,
containerUnit: element.definingCompilationUnit,
);
for (var linkingUnit in units) {
var elementBuilder = FragmentBuilder(
libraryBuilder: this,
unitElement: linkingUnit.element,
);
elementBuilder.buildDirectives(linkingUnit.node);
elementBuilder.buildDeclarationFragments(linkingUnit.node);
if (linkingUnit is DefiningLinkingUnit) {
elementBuilder.buildLibraryMetadata(linkingUnit.node);
}
}
ElementBuilder(libraryBuilder: this).buildElements(
topFragments: _topFragments,
parentChildFragments: _parentChildFragments,
);
_declareDartCoreDynamicNever();
}
void buildEnumChildren() {
var typeProvider = element.typeProvider;
for (var enum_ in implicitEnumNodes.values) {
enum_.element.supertype =
typeProvider.enumType ?? typeProvider.objectType;
var valuesType = typeProvider.listType(
element.typeSystem.instantiateInterfaceToBounds2(
element: enum_.element.asElement2,
nullabilitySuffix: typeProvider.objectType.nullabilitySuffix,
),
);
enum_.valuesTypeNode.type = valuesType;
enum_.valuesElement.type = valuesType;
// TODO(scheglov): We repeat this code.
enum_.valuesElement.element.getter2!.returnType = valuesType;
enum_.valuesElement.element.getter2!.firstFragment.returnType =
valuesType;
}
}
void buildEnumSyntheticConstructors() {
bool hasConstructor(EnumFragmentImpl fragment) {
for (var constructor in fragment.element.constructors) {
if (constructor.isGenerative || constructor.name3 == 'new') {
return true;
}
}
return false;
}
for (var enumFragment in element.topLevelElements) {
if (enumFragment is! EnumFragmentImpl) continue;
if (hasConstructor(enumFragment)) continue;
var fragment =
ConstructorFragmentImpl(name2: 'new', nameOffset: -1)
..isConst = true
..isSynthetic = true;
fragment.typeName = enumFragment.name2;
var element = ConstructorElementImpl(
name3: fragment.name2,
reference: enumFragment.element.reference
.getChild('@constructor')
.addChild('new'),
firstFragment: fragment,
);
enumFragment.element.addConstructor(element);
enumFragment.constructors =
[...enumFragment.constructors, fragment].toFixedList();
}
}
void buildInitialExportScope() {
exportScope = ExportScope();
_declaredReferences.forEach((name, reference) {
if (name.startsWith('_')) return;
if (reference.isPrefix) return;
exportScope.declare(name, reference);
});
}
void collectMixinSuperInvokedNames() {
for (var linkingUnit in units) {
for (var declaration in linkingUnit.node.declarations) {
if (declaration is ast.MixinDeclarationImpl) {
var names = <String>{};
var collector = MixinSuperInvokedNamesCollector(names);
for (var executable in declaration.members) {
if (executable is ast.MethodDeclarationImpl) {
executable.body.accept(collector);
}
}
var fragment = declaration.declaredFragment!;
fragment.superInvokedNames = names.toList();
}
}
}
}
/// Computes which fields in this library are promotable.
void computeFieldPromotability() {
_FieldPromotability(
this,
enabled: element.featureSet.isEnabled(Feature.inference_update_2),
).perform();
}
void declare(Element element, Reference reference) {
if (element.lookupName case var lookupName?) {
_declaredReferences[lookupName] = reference;
}
}
String getReferenceName(String? name) {
return name ?? '${_nextUnnamedId++}';
}
void replaceConstFieldsIfNoConstConstructor() {
var hasConstConstructorCache = <InterfaceElement, bool>{};
bool hasConstConstructor(Element element) {
if (element is InterfaceElement) {
var result = hasConstConstructorCache[element];
if (result == null) {
result = element.constructors.any((e) => e.isConst);
hasConstConstructorCache[element] = result;
}
return result;
}
return false;
}
for (var fieldFragment in finalInstanceFields) {
var enclosingElement = fieldFragment.enclosingFragment.element;
if (!hasConstConstructor(enclosingElement)) {
fieldFragment.constantInitializer = null;
}
}
}
void resolveConstructorFieldFormals() {
for (var interfaceFragment in element.topLevelElements) {
if (interfaceFragment is! InterfaceFragmentImpl) {
continue;
}
if (interfaceFragment is ClassFragmentImpl &&
interfaceFragment.isMixinApplication) {
continue;
}
var element = interfaceFragment.element;
for (var constructor in interfaceFragment.constructors) {
for (var parameter in constructor.parameters) {
if (parameter is FieldFormalParameterFragmentImpl) {
parameter.field =
element.getField(parameter.name2 ?? '')?.asElement;
}
}
}
}
}
void resolveConstructors() {
ConstructorInitializerResolver(linker, this).resolve();
}
void resolveDefaultValues() {
DefaultValueResolver(linker, this).resolve();
}
void resolveMetadata() {
for (var linkingUnit in units) {
var resolver = MetadataResolver(linker, linkingUnit.element, this);
linkingUnit.node.accept(resolver);
}
}
void resolveTypes(NodesToBuildType nodesToBuildType) {
for (var linkingUnit in units) {
var resolver = ReferenceResolver(
linker,
nodesToBuildType,
element.typeSystem,
linkingUnit.element.scope,
);
linkingUnit.node.accept(resolver);
}
}
void setDefaultSupertypes() {
var shouldResetClassHierarchies = false;
var objectType = element.typeProvider.objectType;
for (var interfaceFragment in element.topLevelElements) {
switch (interfaceFragment) {
case ClassFragmentImpl():
if (interfaceFragment.isDartCoreObject) continue;
if (interfaceFragment.supertype == null) {
shouldResetClassHierarchies = true;
interfaceFragment.supertype = objectType;
}
case MixinFragmentImpl():
var element = interfaceFragment.element;
if (element.superclassConstraints.isEmpty) {
shouldResetClassHierarchies = true;
interfaceFragment.superclassConstraints = [objectType];
}
}
}
if (shouldResetClassHierarchies) {
element.session.classHierarchy.removeOfLibraries({uri});
}
}
void storeExportScope() {
element.exportedReferences = exportScope.toReferences();
var definedNames = <String, Element>{};
for (var entry in exportScope.map.entries) {
var reference = entry.value.reference;
var element = linker.elementFactory.elementOfReference3(reference);
definedNames[entry.key] = element;
}
var namespace = Namespace(definedNames);
element.exportNamespace = namespace;
var entryPoint = namespace.get2(TopLevelFunctionElement.MAIN_FUNCTION_NAME);
if (entryPoint is TopLevelFunctionElementImpl) {
element.entryPoint2 = entryPoint;
}
}
List<NamespaceCombinator> _buildCombinators(
List<UnlinkedCombinator> combinators2,
) {
return combinators2.map((unlinked) {
if (unlinked.isShow) {
return ShowElementCombinatorImpl()
..offset = unlinked.keywordOffset
..end = unlinked.endOffset
..shownNames = unlinked.names;
} else {
return HideElementCombinatorImpl()
..offset = unlinked.keywordOffset
..end = unlinked.endOffset
..hiddenNames = unlinked.names;
}
}).toFixedList();
}
/// Builds directive elements, for the library and recursively for its
/// augmentations.
void _buildDirectives({
required FileKind kind,
required LibraryFragmentImpl containerUnit,
}) {
containerUnit.libraryExports =
kind.libraryExports.map((state) {
return _buildLibraryExport(state);
}).toFixedList();
containerUnit.libraryImports =
kind.libraryImports.map((state) {
return _buildLibraryImport(
containerUnit: containerUnit,
state: state,
);
}).toFixedList();
containerUnit.parts =
kind.partIncludes.map((partState) {
return _buildPartInclude(
containerLibrary: element,
containerUnit: containerUnit,
state: partState,
);
}).toFixedList();
}
LibraryExportImpl _buildLibraryExport(LibraryExportState state) {
var combinators = _buildCombinators(state.unlinked.combinators);
DirectiveUri uri;
switch (state) {
case LibraryExportWithFile():
var exportedLibraryKind = state.exportedLibrary;
if (exportedLibraryKind != null) {
var exportedFile = exportedLibraryKind.file;
var exportedUri = exportedFile.uri;
var elementFactory = linker.elementFactory;
var exportedLibrary = elementFactory.libraryOfUri2(exportedUri);
uri = DirectiveUriWithLibraryImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: exportedLibrary.source,
library2: exportedLibrary,
);
} else {
uri = DirectiveUriWithSourceImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: state.exportedSource,
);
}
case LibraryExportWithInSummarySource():
var exportedLibrarySource = state.exportedLibrarySource;
if (exportedLibrarySource != null) {
var exportedUri = exportedLibrarySource.uri;
var elementFactory = linker.elementFactory;
var exportedLibrary = elementFactory.libraryOfUri2(exportedUri);
uri = DirectiveUriWithLibraryImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: exportedLibrary.source,
library2: exportedLibrary,
);
} else {
uri = DirectiveUriWithSourceImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: state.exportedSource,
);
}
default:
var selectedUri = state.selectedUri;
switch (selectedUri) {
case file_state.DirectiveUriWithUri():
uri = DirectiveUriWithRelativeUriImpl(
relativeUriString: selectedUri.relativeUriStr,
relativeUri: selectedUri.relativeUri,
);
case file_state.DirectiveUriWithString():
uri = DirectiveUriWithRelativeUriStringImpl(
relativeUriString: selectedUri.relativeUriStr,
);
default:
uri = DirectiveUriImpl();
}
}
return LibraryExportImpl(
combinators: combinators,
exportKeywordOffset: state.unlinked.exportKeywordOffset,
uri: uri,
);
}
LibraryImportImpl _buildLibraryImport({
required LibraryFragmentImpl containerUnit,
required LibraryImportState state,
}) {
var prefixFragment = state.unlinked.prefix.mapOrNull((unlinked) {
return _buildLibraryImportPrefixFragment(
libraryFragment: containerUnit,
unlinkedName: unlinked.name,
offset: unlinked.nameOffset,
isDeferred: unlinked.deferredOffset != null,
);
});
var combinators = _buildCombinators(state.unlinked.combinators);
DirectiveUri uri;
switch (state) {
case LibraryImportWithFile():
var importedLibraryKind = state.importedLibrary;
if (importedLibraryKind != null) {
var importedFile = importedLibraryKind.file;
var importedUri = importedFile.uri;
var elementFactory = linker.elementFactory;
var importedLibrary = elementFactory.libraryOfUri2(importedUri);
uri = DirectiveUriWithLibraryImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: importedLibrary.source,
library2: importedLibrary,
);
} else {
uri = DirectiveUriWithSourceImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: state.importedSource,
);
}
case LibraryImportWithInSummarySource():
var importedLibrarySource = state.importedLibrarySource;
if (importedLibrarySource != null) {
var importedUri = importedLibrarySource.uri;
var elementFactory = linker.elementFactory;
var importedLibrary = elementFactory.libraryOfUri2(importedUri);
uri = DirectiveUriWithLibraryImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: importedLibrary.source,
library2: importedLibrary,
);
} else {
uri = DirectiveUriWithSourceImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: state.importedSource,
);
}
default:
var selectedUri = state.selectedUri;
switch (selectedUri) {
case file_state.DirectiveUriWithUri():
uri = DirectiveUriWithRelativeUriImpl(
relativeUriString: selectedUri.relativeUriStr,
relativeUri: selectedUri.relativeUri,
);
case file_state.DirectiveUriWithString():
uri = DirectiveUriWithRelativeUriStringImpl(
relativeUriString: selectedUri.relativeUriStr,
);
default:
uri = DirectiveUriImpl();
}
}
return LibraryImportImpl(
isSynthetic: state.isSyntheticDartCore,
combinators: combinators,
importKeywordOffset: state.unlinked.importKeywordOffset,
prefix2: prefixFragment,
uri: uri,
);
}
PrefixFragmentImpl _buildLibraryImportPrefixFragment({
required LibraryFragmentImpl libraryFragment,
required UnlinkedLibraryImportPrefixName? unlinkedName,
required int offset,
required bool isDeferred,
}) {
var fragment = PrefixFragmentImpl(
enclosingFragment: libraryFragment,
name2: unlinkedName?.name,
nameOffset2: unlinkedName?.nameOffset,
isDeferred: isDeferred,
)..offset = offset;
var refName = getReferenceName(unlinkedName?.name);
var reference = this.reference
.getChild('@fragment')
.getChild('${libraryFragment.source.uri}')
.getChild('@prefix2')
.getChild(refName);
var element = reference.element as PrefixElementImpl?;
if (element == null) {
element = PrefixElementImpl(
reference: reference,
firstFragment: fragment,
);
} else {
element.addFragment(fragment);
}
fragment.element = element;
return fragment;
}
PartIncludeImpl _buildPartInclude({
required LibraryElementImpl containerLibrary,
required LibraryFragmentImpl containerUnit,
required file_state.PartIncludeState state,
}) {
DirectiveUriImpl directiveUri;
switch (state) {
case PartIncludeWithFile():
var includedPart = state.includedPart;
if (includedPart != null) {
var partFile = includedPart.file;
var partUnitNode = partFile.parse(
performance: OperationPerformanceImpl('<root>'),
);
var unitElement = LibraryFragmentImpl(
library: containerLibrary,
source: partFile.source,
lineInfo: partUnitNode.lineInfo,
);
partUnitNode.declaredFragment = unitElement;
unitElement.isSynthetic = !partFile.exists;
unitElement.setCodeRange(0, partUnitNode.length);
units.add(LinkingUnit(node: partUnitNode, element: unitElement));
_buildDirectives(kind: includedPart, containerUnit: unitElement);
directiveUri = DirectiveUriWithUnitImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
libraryFragment: unitElement,
);
} else {
directiveUri = DirectiveUriWithSourceImpl(
relativeUriString: state.selectedUri.relativeUriStr,
relativeUri: state.selectedUri.relativeUri,
source: state.includedFile.source,
);
}
default:
var uriState = state.selectedUri;
switch (uriState) {
case file_state.DirectiveUriWithSource():
directiveUri = DirectiveUriWithSourceImpl(
relativeUriString: uriState.relativeUriStr,
relativeUri: uriState.relativeUri,
source: uriState.source,
);
case file_state.DirectiveUriWithUri():
directiveUri = DirectiveUriWithRelativeUriImpl(
relativeUriString: uriState.relativeUriStr,
relativeUri: uriState.relativeUri,
);
case file_state.DirectiveUriWithString():
directiveUri = DirectiveUriWithRelativeUriStringImpl(
relativeUriString: uriState.relativeUriStr,
);
default:
directiveUri = DirectiveUriImpl();
}
}
return PartIncludeImpl(uri: directiveUri);
}
/// We want to have stable references for `loadLibrary` function. But we
/// cannot create the function itself right now, because the type provider
/// might be not available yet. So, we create references together with the
/// library, and create the fragment and element later.
void _createLoadLibraryReferences() {
var name = TopLevelFunctionElement.LOAD_LIBRARY_NAME;
var elementContainer = reference.getChild('@function');
var elementReference = elementContainer.addChild(name);
element.loadLibraryProvider = LoadLibraryFunctionProvider(
elementReference: elementReference,
);
}
/// These elements are implicitly declared in `dart:core`.
void _declareDartCoreDynamicNever() {
if (reference.name == 'dart:core') {
var dynamicRef = reference.getChild('dynamic');
dynamicRef.element = DynamicElementImpl.instance;
declare(DynamicElementImpl.instance, dynamicRef);
var neverRef = reference.getChild('Never');
neverRef.element = NeverElementImpl.instance;
declare(NeverElementImpl.instance, neverRef);
}
}
static void build({
required Linker linker,
required LibraryFileKind inputLibrary,
required OperationPerformanceImpl performance,
}) {
var elementFactory = linker.elementFactory;
var rootReference = linker.rootReference;
var libraryFile = inputLibrary.file;
var libraryUriStr = libraryFile.uriStr;
var libraryReference = rootReference.getChild(libraryUriStr);
var libraryUnitNode = performance.run('libraryFile', (performance) {
return libraryFile.parse(performance: performance);
});
var name = '';
var nameOffset = -1;
var nameLength = 0;
for (var directive in libraryUnitNode.directives) {
if (directive is ast.LibraryDirectiveImpl) {
var nameIdentifier = directive.name2;
if (nameIdentifier != null) {
name = nameIdentifier.components.map((e) => e.name).join('.');
nameOffset = nameIdentifier.offset;
nameLength = nameIdentifier.length;
}
break;
}
}
var libraryElement = LibraryElementImpl(
elementFactory.analysisContext,
elementFactory.analysisSession,
name,
nameOffset,
nameLength,
libraryUnitNode.featureSet,
);
libraryElement.isSynthetic = !libraryFile.exists;
libraryElement.languageVersion = libraryUnitNode.languageVersion;
libraryElement.reference = libraryReference;
libraryReference.element = libraryElement;
var linkingUnits = <LinkingUnit>[];
{
var unitElement = LibraryFragmentImpl(
library: libraryElement,
source: libraryFile.source,
lineInfo: libraryUnitNode.lineInfo,
);
libraryUnitNode.declaredFragment = unitElement;
unitElement.isSynthetic = !libraryFile.exists;
unitElement.setCodeRange(0, libraryUnitNode.length);
linkingUnits.add(
DefiningLinkingUnit(node: libraryUnitNode, element: unitElement),
);
libraryElement.definingCompilationUnit = unitElement;
}
var builder = LibraryBuilder._(
linker: linker,
kind: inputLibrary,
uri: libraryFile.uri,
reference: libraryReference,
element: libraryElement,
units: linkingUnits,
);
builder._createLoadLibraryReferences();
elementFactory.setLibraryTypeSystem(libraryElement);
linker.builders[builder.uri] = builder;
}
}
class LinkingUnit {
final ast.CompilationUnitImpl node;
final LibraryFragmentImpl element;
LinkingUnit({required this.node, required this.element});
}
/// This class examines all the [InterfaceElementImpl]s in a library and
/// determines which fields are promotable within that library.
class _FieldPromotability
extends
FieldPromotability<
InterfaceElementImpl,
FieldElementImpl,
GetterElementImpl
> {
/// The [_libraryBuilder] for the library being analyzed.
final LibraryBuilder _libraryBuilder;
final bool enabled;
/// Fields that might be promotable, if not marked unpromotable later.
final List<FieldElementImpl> _potentiallyPromotableFields = [];
_FieldPromotability(this._libraryBuilder, {required this.enabled});
@override
Iterable<InterfaceElementImpl> getSuperclasses(
InterfaceElementImpl class_, {
required bool ignoreImplements,
}) {
var result = <InterfaceElementImpl>[];
var supertype = class_.supertype;
if (supertype != null) {
result.add(supertype.element);
}
for (var mixin in class_.mixins) {
result.add(mixin.element);
}
if (!ignoreImplements) {
for (var interface in class_.interfaces) {
result.add(interface.element);
}
if (class_ is MixinElementImpl) {
for (var constraint in class_.superclassConstraints) {
result.add(constraint.element);
}
}
}
return result;
}
/// Computes which fields are promotable and updates their `isPromotable`
/// properties accordingly.
void perform() {
// Iterate through all the classes, enums, and mixins in the library,
// recording the non-synthetic instance fields and getters of each.
var element = _libraryBuilder.element;
for (var class_ in element.classes) {
_handleMembers(addClass(class_, isAbstract: class_.isAbstract), class_);
}
for (var enum_ in element.enums) {
_handleMembers(addClass(enum_, isAbstract: false), enum_);
}
for (var mixin_ in element.mixins) {
_handleMembers(addClass(mixin_, isAbstract: true), mixin_);
}
// Private representation fields of extension types are always promotable.
// They also don't affect promotability of any other fields.
for (var extensionType in element.extensionTypes) {
var representation = extensionType.representation;
var representationName = representation.name3;
if (representationName != null) {
if (representationName.startsWith('_')) {
representation.firstFragment.isPromotable = true;
}
}
}
// Compute the set of field names that are not promotable.
var fieldNonPromotabilityInfo = computeNonPromotabilityInfo();
// Set the `isPromotable` bit for each field element that *is* promotable.
for (var field in _potentiallyPromotableFields) {
if (fieldNonPromotabilityInfo[field.name3!] == null) {
field.firstFragment.isPromotable = true;
}
}
element.fieldNameNonPromotabilityInfo = {
for (var MapEntry(:key, :value) in fieldNonPromotabilityInfo.entries)
key: element_model.FieldNameNonPromotabilityInfo(
conflictingFields: value.conflictingFields,
conflictingGetters: value.conflictingGetters,
conflictingNsmClasses: value.conflictingNsmClasses,
),
};
}
/// Records all the non-synthetic instance fields and getters of [class_]
/// into [classInfo].
void _handleMembers(
ClassInfo<InterfaceElementImpl> classInfo,
InterfaceElementImpl class_,
) {
for (var field in class_.fields) {
if (field.isStatic || field.isSynthetic) {
continue;
}
var fieldName = field.name3;
if (fieldName != null) {
var nonPromotabilityReason = addField(
classInfo,
field,
fieldName,
isFinal: field.isFinal,
isAbstract: field.isAbstract,
isExternal: field.isExternal,
);
if (enabled && nonPromotabilityReason == null) {
_potentiallyPromotableFields.add(field);
}
}
}
for (var getter in class_.getters) {
if (getter.isStatic || getter.isSynthetic) {
continue;
}
var getterName = getter.name3;
if (getterName != null) {
var nonPromotabilityReason = addGetter(
classInfo,
getter,
getterName,
isAbstract: getter.isAbstract,
);
if (enabled && nonPromotabilityReason == null) {
var field = getter.variable3 as FieldElementImpl;
_potentiallyPromotableFields.add(field);
}
}
}
}
}
extension<T> on T? {
R? mapOrNull<R>(R Function(T) mapper) {
var self = this;
return self != null ? mapper(self) : null;
}
}