blob: 20274d0ef7825c447b67c9156f708e76b8bada5f [file] [log] [blame]
// Copyright (c) 2025, 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 'dart:typed_data';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/fine/base_name_members.dart';
import 'package:analyzer/src/fine/lookup_name.dart';
import 'package:analyzer/src/fine/manifest_context.dart';
import 'package:analyzer/src/fine/manifest_id.dart';
import 'package:analyzer/src/fine/manifest_item.dart';
import 'package:analyzer/src/summary2/data_reader.dart';
import 'package:analyzer/src/summary2/data_writer.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:collection/collection.dart';
/// The manifest of a single library.
class LibraryManifest {
/// The names that are re-exported by this library.
/// This does not include names that are declared in this library.
final Map<LookupName, ManifestItemId> reExportMap;
/// The manifests of the top-level items.
final Map<LookupName, TopLevelItem> items;
LibraryManifest({required this.reExportMap, required this.items});
factory LibraryManifest.read(SummaryDataReader reader) {
return LibraryManifest(
reExportMap: reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => ManifestItemId.read(reader),
),
items: reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => TopLevelItem.read(reader),
),
);
}
/// Returns the ID of a top-level element either declared or re-exported,
/// or `null` if there is no such element.
ManifestItemId? getExportedId(LookupName name) {
return items[name]?.id ?? reExportMap[name];
}
void write(BufferedSink sink) {
sink.writeMap(
reExportMap,
writeKey: (lookupName) => lookupName.write(sink),
writeValue: (id) => id.write(sink),
);
sink.writeMap(
items,
writeKey: (lookupName) => lookupName.write(sink),
writeValue: (item) => item.write(sink),
);
}
}
class LibraryManifestBuilder {
final LinkedElementFactory elementFactory;
final List<LibraryFileKind> inputLibraries;
late final List<LibraryElementImpl> libraryElements;
/// The previous manifests for libraries.
///
/// For correctness it does not matter what is in these manifests, they
/// can be absent at all for any library (and they are for new libraries).
/// But it does matter for performance, because we will give a new ID for any
/// element that is not in the manifest, or have different "meaning". This
/// will cause cascading changes to items that referenced these elements,
/// new IDs for them, etc.
final Map<Uri, LibraryManifest> inputManifests;
/// Key: an element from [inputLibraries].
/// Value: the item from [inputManifests], or newly build.
///
/// We attempt to reuse the same item, most importantly its ID.
///
/// It is filled initially during matching element structures.
/// Then we remove those that affected by changed elements.
///
/// Then we iterate over the elements in [libraryElements], and build new
/// items for declared elements that don't have items in this map.
final Map<Element2, ManifestItem> declaredItems = Map.identity();
/// The new manifests for libraries.
final Map<Uri, LibraryManifest> newManifests = {};
LibraryManifestBuilder({
required this.elementFactory,
required this.inputLibraries,
required this.inputManifests,
}) {
libraryElements = inputLibraries
.map((kind) {
return elementFactory.libraryOfUri2(kind.file.uri);
})
.toList(growable: false);
}
Map<Uri, LibraryManifest> computeManifests({
required OperationPerformanceImpl performance,
}) {
performance.getDataInt('libraryCount').add(inputLibraries.length);
_fillItemMapFromInputManifests(performance: performance);
_buildManifests();
_addReExports();
assert(_assertSerialization());
return newManifests;
}
void _addClass({
required EncodeContext encodingContext,
required Map<LookupName, TopLevelItem> newItems,
required ClassElementImpl2 element,
required LookupName lookupName,
}) {
var classItem = _getOrBuildElementItem(element, () {
return ClassItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
newItems[lookupName] = classItem;
encodingContext.withTypeParameters(element.typeParameters2, (
typeParameters,
) {
classItem.members.clear();
_addInterfaceElementExecutables(
encodingContext: encodingContext,
instanceElement: element,
interfaceItem: classItem,
);
});
}
/// Class type aliases like `class B = A with M;` cannot explicitly declare
/// any members. They have constructors, but these are based on the
/// constructors of the supertype, and change if the supertype constructors
/// change. So, it is enough to record that supertype constructors into
/// the manifest.
void _addClassTypeAliasConstructors() {
var hasConstructors = <ClassElementImpl2>{};
var inheritedMap = <ConstructorElementImpl2, ManifestItemId>{};
void addForElement(ClassElementImpl2 element) {
if (!element.isMixinApplication) {
return;
}
// We might have already processed this element due to recursion.
if (!hasConstructors.add(element)) {
return;
}
// SAFETY: all items are already created.
var item = declaredItems[element] as ClassItem;
// SAFETY: we set `Object` during linking if it is not a class.
var superElement = element.supertype!.element3;
superElement as ClassElementImpl2;
// The supertype could be a mixin application itself.
addForElement(superElement);
for (var constructor in element.constructors2) {
var lookupName = constructor.lookupName?.asLookupName;
if (lookupName == null) {
continue;
}
// SAFETY: we build inherited constructors from existing super.
var superConstructor = constructor.superConstructor2!.baseElement;
// Maybe the super constructor is "inherited" itself.
var id = inheritedMap[superConstructor];
// If not inherited, then must be declared.
id ??= _getInterfaceElementMemberId(superConstructor);
inheritedMap[constructor] = id;
var baseName = lookupName.asBaseName;
item.members.addInheritedConstructor(baseName, id);
}
}
for (var libraryElement in libraryElements) {
for (var element in libraryElement.children2) {
if (element is ClassElementImpl2) {
addForElement(element);
}
}
}
}
void _addInheritedInterfaceElementExecutables(InterfaceElementImpl2 element) {
// We don't create items for elements without name.
if (element.lookupName == null) {
return;
}
// Must be created already.
var item = declaredItems[element] as InterfaceItem;
var map = element.inheritanceManager.getInterface2(element).map2;
for (var entry in map.entries) {
var executable = entry.value.baseElement;
// Add only inherited.
if (executable.enclosingElement2 == element) {
continue;
}
var lookupName = executable.lookupName?.asLookupName;
if (lookupName == null) {
continue;
}
var id = _getInterfaceElementMemberId(executable);
var baseName = lookupName.asBaseName;
switch (executable) {
case MethodElementImpl2():
if (lookupName.isIndexEq) {
item.members.addInheritedIndexEq(baseName, id);
} else {
item.members.addInheritedMethod(baseName, id);
}
case GetterElementImpl():
item.members.addInheritedGetter(baseName, id);
case SetterElementImpl():
item.members.addInheritedSetter(baseName, id);
}
}
}
void _addInheritedInterfaceElementsExecutables() {
for (var libraryElement in libraryElements) {
for (var element in libraryElement.children2) {
if (element is InterfaceElementImpl2) {
_addInheritedInterfaceElementExecutables(element);
}
}
}
}
void _addInstanceElementExecutables({
required EncodeContext encodingContext,
required InstanceElementImpl2 instanceElement,
required InstanceItem instanceItem,
}) {
for (var method in instanceElement.methods2) {
_addInstanceElementMethod(
encodingContext: encodingContext,
instanceItem: instanceItem,
element: method,
);
}
for (var getter in instanceElement.getters2) {
_addInstanceElementGetter(
encodingContext: encodingContext,
instanceItem: instanceItem,
element: getter,
);
}
for (var setter in instanceElement.setters2) {
_addInstanceElementSetter(
encodingContext: encodingContext,
instanceItem: instanceItem,
element: setter,
);
}
}
void _addInstanceElementGetter({
required EncodeContext encodingContext,
required InstanceItem instanceItem,
required GetterElementImpl element,
}) {
var lookupName = element.lookupName?.asLookupName;
if (lookupName == null) {
return;
}
var item = _getOrBuildElementItem(element, () {
return InstanceItemGetterItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
var baseName = lookupName.asBaseName;
instanceItem.members.addDeclaredGetter(baseName, item);
}
void _addInstanceElementMethod({
required EncodeContext encodingContext,
required InstanceItem instanceItem,
required MethodElementImpl2 element,
}) {
var lookupName = element.lookupName?.asLookupName;
if (lookupName == null) {
return;
}
var item = _getOrBuildElementItem(element, () {
return InstanceItemMethodItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
var baseName = lookupName.asBaseName;
if (lookupName.isIndexEq) {
instanceItem.members.addDeclaredIndexEq(baseName, item);
} else {
instanceItem.members.addDeclaredMethod(baseName, item);
}
}
void _addInstanceElementSetter({
required EncodeContext encodingContext,
required InstanceItem instanceItem,
required SetterElementImpl element,
}) {
var lookupName = element.lookupName?.asLookupName;
if (lookupName == null) {
return;
}
var item = _getOrBuildElementItem(element, () {
return InstanceItemSetterItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
var baseName = lookupName.asBaseName;
instanceItem.members.addDeclaredSetter(baseName, item);
}
void _addInterfaceElementConstructor({
required EncodeContext encodingContext,
required InterfaceItem interfaceItem,
required ConstructorElementImpl2 element,
}) {
var lookupName = element.lookupName?.asLookupName;
if (lookupName == null) {
return;
}
var item = _getOrBuildElementItem(element, () {
return InterfaceItemConstructorItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
var baseName = lookupName.asBaseName;
interfaceItem.members.addDeclaredConstructor(baseName, item);
}
void _addInterfaceElementExecutables({
required EncodeContext encodingContext,
required InterfaceElementImpl2 instanceElement,
required InterfaceItem interfaceItem,
}) {
// Class type aliases don't have declared members.
// We don't consider constructors as declared.
if (instanceElement is ClassElementImpl2 &&
instanceElement.isMixinApplication) {
return;
}
for (var constructor in instanceElement.constructors2) {
_addInterfaceElementConstructor(
encodingContext: encodingContext,
interfaceItem: interfaceItem,
element: constructor,
);
}
_addInstanceElementExecutables(
encodingContext: encodingContext,
instanceElement: instanceElement,
instanceItem: interfaceItem,
);
}
void _addMixin({
required EncodeContext encodingContext,
required Map<LookupName, TopLevelItem> newItems,
required MixinElementImpl2 element,
required LookupName lookupName,
}) {
var mixinItem = _getOrBuildElementItem(element, () {
return MixinItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
newItems[lookupName] = mixinItem;
encodingContext.withTypeParameters(element.typeParameters2, (
typeParameters,
) {
mixinItem.members.clear();
_addInterfaceElementExecutables(
encodingContext: encodingContext,
instanceElement: element,
interfaceItem: mixinItem,
);
});
}
void _addReExports() {
for (var libraryElement in libraryElements) {
var libraryUri = libraryElement.uri;
var manifest = newManifests[libraryUri]!;
for (var entry in libraryElement.exportNamespace.definedNames2.entries) {
var name = entry.key.asLookupName;
var element = entry.value;
// Skip elements that exist in nowhere.
var elementLibraryUri = element.library2?.uri;
if (elementLibraryUri == null) {
continue;
}
// Skip elements declared in this library.
if (elementLibraryUri == libraryUri) {
continue;
}
// Skip if the element is declared in this library.
if (element.library2 == libraryElement) {
continue;
}
// Maybe exported from a library outside the current cycle.
var id = elementFactory.getElementId(element);
// If not, then look into new manifest.
if (id == null) {
var newManifest = newManifests[elementLibraryUri];
// Maybe declared in this library.
id ??= newManifest?.items[name]?.id;
// Maybe exported from this library.
// TODO(scheglov): repeat for re-re-exports
id ??= newManifest?.reExportMap[name];
}
if (id == null) {
// TODO(scheglov): complete
continue;
}
manifest.reExportMap[name] = id;
}
}
}
void _addTopLevelFunction({
required EncodeContext encodingContext,
required Map<LookupName, TopLevelItem> newItems,
required TopLevelFunctionElementImpl element,
required LookupName lookupName,
}) {
var item = _getOrBuildElementItem(element, () {
return TopLevelFunctionItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
newItems[lookupName] = item;
}
void _addTopLevelGetter({
required EncodeContext encodingContext,
required Map<LookupName, TopLevelItem> newItems,
required GetterElementImpl element,
required LookupName lookupName,
}) {
var item = _getOrBuildElementItem(element, () {
return TopLevelGetterItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
newItems[lookupName] = item;
}
void _addTopLevelSetter({
required EncodeContext encodingContext,
required Map<LookupName, TopLevelItem> newItems,
required SetterElementImpl element,
required LookupName lookupName,
}) {
var item = _getOrBuildElementItem(element, () {
return TopLevelSetterItem.fromElement(
id: ManifestItemId.generate(),
context: encodingContext,
element: element,
);
});
newItems[lookupName] = item;
}
/// Assert that every manifest can be serialized, and when deserialized
/// results in the same manifest.
bool _assertSerialization() {
Uint8List manifestAsBytes(LibraryManifest manifest) {
var byteSink = BufferedSink();
manifest.write(byteSink);
return byteSink.takeBytes();
}
newManifests.forEach((uri, manifest) {
var bytes = manifestAsBytes(manifest);
var readManifest = LibraryManifest.read(SummaryDataReader(bytes));
var readBytes = manifestAsBytes(readManifest);
if (!const ListEquality<int>().equals(bytes, readBytes)) {
throw StateError('Library manifest bytes are different: $uri');
}
});
return true;
}
/// Fill `result` with new library manifests.
/// We reuse existing items when they fully match.
/// We build new items for mismatched elements.
Map<Uri, LibraryManifest> _buildManifests() {
var encodingContext = EncodeContext(elementFactory: elementFactory);
for (var libraryElement in libraryElements) {
var libraryUri = libraryElement.uri;
var newItems = <LookupName, TopLevelItem>{};
for (var element in libraryElement.children2) {
var lookupName = element.lookupName?.asLookupName;
if (lookupName == null) {
continue;
}
switch (element) {
case ClassElementImpl2():
_addClass(
encodingContext: encodingContext,
newItems: newItems,
element: element,
lookupName: lookupName,
);
case GetterElementImpl():
_addTopLevelGetter(
encodingContext: encodingContext,
newItems: newItems,
element: element,
lookupName: lookupName,
);
case MixinElementImpl2():
_addMixin(
encodingContext: encodingContext,
newItems: newItems,
element: element,
lookupName: lookupName,
);
case SetterElementImpl():
_addTopLevelSetter(
encodingContext: encodingContext,
newItems: newItems,
element: element,
lookupName: lookupName,
);
case TopLevelFunctionElementImpl():
_addTopLevelFunction(
encodingContext: encodingContext,
newItems: newItems,
element: element,
lookupName: lookupName,
);
// TODO(scheglov): add remaining elements
}
}
var newManifest = LibraryManifest(reExportMap: {}, items: newItems);
libraryElement.manifest = newManifest;
newManifests[libraryUri] = newManifest;
}
_addInheritedInterfaceElementsExecutables();
_addClassTypeAliasConstructors();
return newManifests;
}
void _fillItemMapFromInputManifests({
required OperationPerformanceImpl performance,
}) {
// Compare structures of the elements against the existing manifests.
// At the end `affectedElements` is filled with mismatched by structure.
// And for matched by structure we have reference maps.
var refElementsMap = Map<Element2, List<Element2>>.identity();
var refExternalIds = Map<Element2, ManifestItemId>.identity();
var affectedElements = Set<Element2>.identity();
for (var libraryElement in libraryElements) {
var libraryUri = libraryElement.uri;
var manifest = _getInputManifest(libraryUri);
_LibraryMatch(
manifest: manifest,
library: libraryElement,
itemMap: declaredItems,
structureMismatched: affectedElements,
refElementsMap: refElementsMap,
refExternalIds: refExternalIds,
).compareStructures();
}
performance
..getDataInt('structureMatchedCount').add(declaredItems.length)
..getDataInt('structureMismatchedCount').add(affectedElements.length);
// Propagate invalidation from referenced elements.
// Both from external elements, and from input library elements.
for (var element in refElementsMap.keys.toList()) {
var refElements = refElementsMap[element];
if (refElements != null) {
for (var referencedElement in refElements) {
// If the referenced element is from this bundle, and is determined
// to be affected, this makes the current element affected.
if (affectedElements.contains(referencedElement)) {
// Move the element to affected.
// Its dependencies are not interesting anymore.
affectedElements.add(element);
declaredItems.remove(element);
refElementsMap.remove(element);
break;
}
// Maybe has a different external id.
var requiredExternalId = refExternalIds[referencedElement];
if (requiredExternalId != null) {
var currentId = elementFactory.getElementId(referencedElement);
if (currentId != requiredExternalId) {
// Move the element to affected.
// Its dependencies are not interesting anymore.
affectedElements.add(element);
declaredItems.remove(element);
refElementsMap.remove(element);
break;
}
}
}
}
}
performance
..getDataInt('transitiveMatchedCount').add(declaredItems.length)
..getDataInt('transitiveAffectedCount').add(affectedElements.length);
}
/// Returns the manifest from [inputManifests], empty if absent.
LibraryManifest _getInputManifest(Uri uri) {
return inputManifests[uri] ?? LibraryManifest(reExportMap: {}, items: {});
}
ManifestItemId _getInterfaceElementMemberId(ExecutableElementImpl2 element) {
if (declaredItems[element] case var declaredItem?) {
return declaredItem.id;
}
return elementFactory.getElementId(element)!;
}
/// Returns either the existing item from [declaredItems], or builds a new one.
Item _getOrBuildElementItem<
Element extends Element2,
Item extends ManifestItem
>(Element element, Item Function() build) {
// We assume that when matching elements against the structure of
// the item, we put into [itemMap] only the type of the item that
// corresponds the type of the element.
var item = declaredItems[element] as Item?;
if (item == null) {
item = build();
// To find IDs of inherited members.
declaredItems[element] = item;
}
return item;
}
}
/// Compares structures of [library] children against [manifest].
class _LibraryMatch {
final LibraryElementImpl library;
/// A previous manifest for the [library].
///
/// Strictly speaking, it does not have to be the latest manifest, it could
/// be empty at all (and is empty when this is a new library). It is used
/// to give the same identifiers to the elements with the same meaning.
final LibraryManifest manifest;
/// Elements that have structure matching the corresponding items from
/// [manifest].
final Map<Element2, ManifestItem> itemMap;
/// Elements with mismatched structure.
/// These elements will get new identifiers.
final Set<Element2> structureMismatched;
/// Key: an element of [library].
/// Value: the elements that the key references.
///
/// This includes references to elements of this bundle, and of external
/// bundles. This information allows propagating invalidation from affected
/// elements to their dependents.
// TODO(scheglov): hm... maybe store it? And reverse it.
final Map<Element2, List<Element2>> refElementsMap;
/// Key: an element from an external bundle.
/// Value: the identifier at the time when [manifest] was built.
///
/// If [LibraryManifestBuilder] later finds that some of these elements now
/// have different identifiers, it propagates invalidation using
/// [refElementsMap].
final Map<Element2, ManifestItemId> refExternalIds;
_LibraryMatch({
required this.manifest,
required this.library,
required this.itemMap,
required this.refElementsMap,
required this.refExternalIds,
required this.structureMismatched,
});
void compareStructures() {
for (var element in library.children2) {
var name = element.lookupName?.asLookupName;
switch (element) {
case ClassElementImpl2():
if (!_matchClass(name: name, element: element)) {
structureMismatched.add(element);
}
case GetterElementImpl():
if (!_matchTopGetter(name: name, element: element)) {
structureMismatched.add(element);
}
case MixinElementImpl2():
if (!_matchMixin(name: name, element: element)) {
structureMismatched.add(element);
}
case SetterElementImpl():
if (!_matchTopSetter(name: name, element: element)) {
structureMismatched.add(element);
}
case TopLevelFunctionElementImpl():
if (!_matchTopFunction(name: name, element: element)) {
structureMismatched.add(element);
}
}
}
}
/// Records [item] as matching [element], and stores dependencies.
///
/// The fact that it does match is checked outside.
void _addMatchingElementItem(
ElementImpl2 element,
ManifestItem item,
MatchContext matchContext,
) {
itemMap[element] = item;
refElementsMap[element] = matchContext.elementList;
refExternalIds.addAll(matchContext.externalIds);
}
bool _matchClass({
required LookupName? name,
required ClassElementImpl2 element,
}) {
var item = manifest.items[name];
if (item is! ClassItem) {
return false;
}
var matchContext = MatchContext(parent: null);
if (!item.match(matchContext, element)) {
return false;
}
_addMatchingElementItem(element, item, matchContext);
_matchInterfaceElementConstructors(
matchContext: matchContext,
interfaceElement: element,
item: item,
);
_matchInstanceElementStaticExecutables(
matchContext: matchContext,
element: element,
item: item,
);
_matchInterfaceElementInstanceExecutables(
matchContext: matchContext,
element: element,
item: item,
);
return true;
}
bool _matchInstanceElementExecutable({
required MatchContext interfaceMatchContext,
required Map<BaseName, BaseNameMembers> members,
required ExecutableElementImpl2 executable,
}) {
var lookupName = executable.lookupName?.asLookupName;
if (lookupName == null) {
return true;
}
var baseName = lookupName.asBaseName;
switch (executable) {
case GetterElementImpl():
var item = members[baseName]?.declaredGetter;
if (item is! InstanceItemGetterItem) {
return false;
}
var matchContext = MatchContext(parent: interfaceMatchContext);
if (!item.match(matchContext, executable)) {
return false;
}
_addMatchingElementItem(executable, item, matchContext);
return true;
case MethodElementImpl2():
var item =
lookupName.isIndexEq
? members[baseName]?.declaredIndexEq
: members[baseName]?.declaredMethod;
if (item is! InstanceItemMethodItem) {
return false;
}
var matchContext = MatchContext(parent: interfaceMatchContext);
if (!item.match(matchContext, executable)) {
return false;
}
_addMatchingElementItem(executable, item, matchContext);
return true;
case SetterElementImpl():
var item = members[baseName]?.declaredSetter;
if (item is! InstanceItemSetterItem) {
return false;
}
var matchContext = MatchContext(parent: interfaceMatchContext);
if (!item.match(matchContext, executable)) {
return false;
}
_addMatchingElementItem(executable, item, matchContext);
return true;
default:
// SAFETY: the cases above handle all expected executables.
throw StateError('(${executable.runtimeType}) $executable');
}
}
void _matchInstanceElementStaticExecutables({
required MatchContext matchContext,
required InstanceElementImpl2 element,
required InstanceItem item,
}) {
var executables = [
...element.getters2,
...element.methods2,
...element.setters2,
];
for (var executable in executables) {
if (executable.isStatic) {
if (!_matchInstanceElementExecutable(
interfaceMatchContext: matchContext,
members: item.members,
executable: executable,
)) {
structureMismatched.add(executable);
}
}
}
}
bool _matchInterfaceElementConstructor({
required MatchContext interfaceMatchContext,
required Map<BaseName, BaseNameMembers> members,
required ConstructorElementImpl2 element,
}) {
var lookupName = element.lookupName?.asLookupName;
if (lookupName == null) {
return false;
}
var baseName = lookupName.asBaseName;
var item = members[baseName]?.declaredConstructor;
if (item is! InterfaceItemConstructorItem) {
return false;
}
var matchContext = MatchContext(parent: interfaceMatchContext);
if (!item.match(matchContext, element)) {
return false;
}
_addMatchingElementItem(element, item, matchContext);
return true;
}
void _matchInterfaceElementConstructors({
required MatchContext matchContext,
required InterfaceElementImpl2 interfaceElement,
required InterfaceItem item,
}) {
for (var constructor in interfaceElement.constructors2) {
if (!_matchInterfaceElementConstructor(
interfaceMatchContext: matchContext,
members: item.members,
element: constructor,
)) {
structureMismatched.add(constructor);
}
}
}
void _matchInterfaceElementInstanceExecutables({
required MatchContext matchContext,
required InterfaceElementImpl2 element,
required InterfaceItem item,
}) {
var map = element.inheritanceManager.getInterface2(element).map2;
for (var executable in map.values) {
if (executable.enclosingElement2 == element) {
// SAFETY: declared in the element are always impl.
executable as ExecutableElementImpl2;
if (!_matchInstanceElementExecutable(
interfaceMatchContext: matchContext,
members: item.members,
executable: executable,
)) {
structureMismatched.add(executable);
}
}
}
}
bool _matchMixin({
required LookupName? name,
required MixinElementImpl2 element,
}) {
var item = manifest.items[name];
if (item is! MixinItem) {
return false;
}
var matchContext = MatchContext(parent: null);
if (!item.match(matchContext, element)) {
return false;
}
_addMatchingElementItem(element, item, matchContext);
_matchInstanceElementStaticExecutables(
matchContext: matchContext,
element: element,
item: item,
);
_matchInterfaceElementInstanceExecutables(
matchContext: matchContext,
element: element,
item: item,
);
return true;
}
bool _matchTopFunction({
required LookupName? name,
required TopLevelFunctionElementImpl element,
}) {
var item = manifest.items[name];
if (item is! TopLevelFunctionItem) {
return false;
}
var matchContext = MatchContext(parent: null);
if (!item.match(matchContext, element)) {
return false;
}
_addMatchingElementItem(element, item, matchContext);
return true;
}
bool _matchTopGetter({
required LookupName? name,
required GetterElementImpl element,
}) {
var item = manifest.items[name];
if (item is! TopLevelGetterItem) {
return false;
}
var matchContext = MatchContext(parent: null);
if (!item.match(matchContext, element)) {
return false;
}
_addMatchingElementItem(element, item, matchContext);
return true;
}
bool _matchTopSetter({
required LookupName? name,
required SetterElementImpl element,
}) {
var item = manifest.items[name];
if (item is! TopLevelSetterItem) {
return false;
}
var matchContext = MatchContext(parent: null);
if (!item.match(matchContext, element)) {
return false;
}
_addMatchingElementItem(element, item, matchContext);
return true;
}
}