in lib/src/model/package_graph.dart [742:870]
ModelElement? findCanonicalModelElementFor(Element? e,
{Container? preferredClass}) {
assert(allLibrariesAdded);
var lib = findCanonicalLibraryFor(e);
if (preferredClass != null) {
var canonicalClass =
findCanonicalModelElementFor(preferredClass.element) as Container?;
if (canonicalClass != null) preferredClass = canonicalClass;
}
if (lib == null && preferredClass != null) {
lib = findCanonicalLibraryFor(preferredClass.element);
}
// For elements defined in extensions, they are canonical.
var enclosingElement = e?.enclosingElement;
if (enclosingElement is ExtensionElement) {
lib ??= modelBuilder.fromElement(enclosingElement.library) as Library?;
// (TODO:keertip) Find a better way to exclude members of extensions
// when libraries are specified using the "--include" flag
if (lib?.isDocumented == true) {
return modelBuilder.from(e!, lib!);
}
}
ModelElement? modelElement;
// TODO(jcollins-g): Special cases are pretty large here. Refactor to split
// out into helpers.
// TODO(jcollins-g): The data structures should be changed to eliminate guesswork
// with member elements.
var declaration = e?.declaration;
if (declaration != null &&
(e is ClassMemberElement || e is PropertyAccessorElement)) {
e = declaration;
var candidates = <ModelElement>{};
var iKey = Tuple2<Element, Library?>(e, lib);
var key =
Tuple4<Element, Library?, Class?, ModelElement?>(e, lib, null, null);
var keyWithClass = Tuple4<Element, Library?, Class?, ModelElement?>(
e, lib, preferredClass as Class?, null);
var constructedWithKey = allConstructedModelElements[key];
if (constructedWithKey != null) {
candidates.add(constructedWithKey);
}
var constructedWithKeyWithClass =
allConstructedModelElements[keyWithClass];
if (constructedWithKeyWithClass != null) {
candidates.add(constructedWithKeyWithClass);
}
if (candidates.isEmpty && allInheritableElements.containsKey(iKey)) {
candidates.addAll(
allInheritableElements[iKey as Tuple2<Element, Library>]!
.where((me) => me.isCanonical));
}
var canonicalClass = findCanonicalModelElementFor(e.enclosingElement);
if (canonicalClass is Class) {
candidates.addAll(canonicalClass.allCanonicalModelElements.where((m) {
return m.element == e;
}));
}
var matches = <ModelElement>{...candidates.where((me) => me.isCanonical)};
// It's possible to find accessors but no combos. Be sure that if we
// have Accessors, we find their combos too.
if (matches.any((me) => me is Accessor)) {
var combos =
matches.whereType<Accessor>().map((a) => a.enclosingCombo).toList();
matches.addAll(combos);
assert(combos.every((c) => c.isCanonical));
}
// This is for situations where multiple classes may actually be canonical
// for an inherited element whose defining Class is not canonical.
if (matches.length > 1 && preferredClass != null) {
// Search for matches inside our superchain.
var superChain = preferredClass.superChain
.map((et) => et.modelElement)
.cast<Class>()
.toList();
superChain.add(preferredClass);
matches.removeWhere((me) =>
!superChain.contains((me as EnclosedElement).enclosingElement));
// Assumed all matches are EnclosedElement because we've been told about a
// preferredClass.
var enclosingElements = {
...matches
.map((me) => (me as EnclosedElement).enclosingElement as Class?)
};
for (var c in superChain.reversed) {
if (enclosingElements.contains(c)) {
matches.removeWhere(
(me) => (me as EnclosedElement).enclosingElement != c);
}
if (matches.length <= 1) break;
}
}
// Prefer a GetterSetterCombo to Accessors.
if (matches.any((me) => me is GetterSetterCombo)) {
matches.removeWhere((me) => me is Accessor);
}
assert(matches.length <= 1);
if (matches.isNotEmpty) {
modelElement = matches.first;
}
} else {
if (lib != null) {
if (e is PropertyInducingElement) {
var getter =
e.getter != null ? modelBuilder.from(e.getter!, lib) : null;
var setter =
e.setter != null ? modelBuilder.from(e.setter!, lib) : null;
modelElement = modelBuilder.fromPropertyInducingElement(e, lib,
getter: getter as Accessor?, setter: setter as Accessor?);
} else {
modelElement = modelBuilder.from(e!, lib);
}
}
assert(modelElement is! Inheritable);
if (modelElement != null && !modelElement.isCanonical) {
modelElement = null;
}
}
// Prefer Fields.
if (e is PropertyAccessorElement && modelElement is Accessor) {
modelElement = modelElement.enclosingCombo;
}
return modelElement;
}