diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index 983579b218..c7ad20b463 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -301,8 +301,7 @@ abstract class DefinedElementType extends ElementType { @override bool get isPublic { var canonicalClass = - packageGraph.findCanonicalModelElementFor(modelElement.element) ?? - modelElement; + packageGraph.findCanonicalModelElementFor(modelElement) ?? modelElement; return canonicalClass.isPublic; } diff --git a/lib/src/generator/templates.runtime_renderers.dart b/lib/src/generator/templates.runtime_renderers.dart index 29a80d00da..bfdb73c782 100644 --- a/lib/src/generator/templates.runtime_renderers.dart +++ b/lib/src/generator/templates.runtime_renderers.dart @@ -3424,7 +3424,7 @@ class _Renderer_ContainerMember extends RendererBase { renderVariable: (CT_ c, Property self, List remainingNames) => self.renderSimpleVariable( - c, remainingNames, 'Iterable'), + c, remainingNames, 'List'), renderIterable: (CT_ c, RendererBase r, List ast, StringSink sink) { return c.referenceGrandparentOverrides.map((e) => diff --git a/lib/src/model/container_member.dart b/lib/src/model/container_member.dart index af8b9e08c3..7ffbb9f1a7 100644 --- a/lib/src/model/container_member.dart +++ b/lib/src/model/container_member.dart @@ -40,16 +40,8 @@ mixin ContainerMember on ModelElement { return canonicalEnclosingContainer; }(); - Container? computeCanonicalEnclosingContainer() { - final enclosingElement = this.enclosingElement; - return switch (enclosingElement) { - Extension() => - packageGraph.findCanonicalModelElementFor(enclosingElement.element), - ExtensionType() => - packageGraph.findCanonicalModelElementFor(enclosingElement.element), - _ => packageGraph.findCanonicalModelElementFor(element.enclosingElement), - } as Container?; - } + Container? computeCanonicalEnclosingContainer() => + packageGraph.findCanonicalModelElementFor(enclosingElement) as Container?; @override // TODO(jcollins-g): dart-lang/dartdoc#2693. @@ -63,13 +55,12 @@ mixin ContainerMember on ModelElement { ]; @override - Iterable get referenceGrandparentOverrides sync* { - // TODO(jcollins-g): split Field documentation up between accessors - // and resolve the pieces with different scopes. dart-lang/dartdoc#2693. - // Until then, just pretend we're handling this correctly. - yield (documentationFrom.first as ModelElement).definingLibrary; - // TODO(jcollins-g): Wean users off of depending on canonical library - // resolution. dart-lang/dartdoc#2696 - if (canonicalLibrary != null) yield canonicalLibrary!; - } + List get referenceGrandparentOverrides => + // TODO(jcollins-g): split Field documentation up between accessors + // and resolve the pieces with different scopes. dart-lang/dartdoc#2693. + // Until then, just pretend we're handling this correctly. + [ + (documentationFrom.first as ModelElement).definingLibrary, + (packageGraph.findCanonicalModelElementFor(this) ?? this).library, + ]; } diff --git a/lib/src/model/inheritable.dart b/lib/src/model/inheritable.dart index 661fc2b15c..94f03ef7f1 100644 --- a/lib/src/model/inheritable.dart +++ b/lib/src/model/inheritable.dart @@ -70,8 +70,8 @@ mixin Inheritable on ContainerMember { .memberByExample(this) .canonicalEnclosingContainer; } - var canonicalContainer = packageGraph - .findCanonicalModelElementFor(c.element) as Container?; + var canonicalContainer = + packageGraph.findCanonicalModelElementFor(c) as Container?; // TODO(jcollins-g): invert this lookup so traversal is recursive // starting from the ModelElement. if (canonicalContainer != null) { @@ -100,7 +100,7 @@ mixin Inheritable on ContainerMember { } } else if (definingEnclosingContainer is! Extension) { // TODO(jcollins-g): factor out extension logic into [Extendable]. - return packageGraph.findCanonicalModelElementFor(element.enclosingElement) + return packageGraph.findCanonicalModelElementFor(enclosingElement) as Container?; } return super.computeCanonicalEnclosingContainer(); diff --git a/lib/src/model/inheriting_container.dart b/lib/src/model/inheriting_container.dart index 30538f314d..708772671f 100644 --- a/lib/src/model/inheriting_container.dart +++ b/lib/src/model/inheriting_container.dart @@ -405,18 +405,28 @@ abstract class InheritingContainer extends Container { } else { var implementers = packageGraph.implementers[implementer]; if (implementers != null) { - model_utils.findCanonicalFor(implementers).forEach(addToResult); + _findCanonicalFor(implementers).forEach(addToResult); } } } var immediateImplementers = packageGraph.implementers[this]; if (immediateImplementers != null) { - model_utils.findCanonicalFor(immediateImplementers).forEach(addToResult); + _findCanonicalFor(immediateImplementers).forEach(addToResult); } return result.toList(growable: false)..sort(byName); } + /// Finds canonical classes for all classes in the iterable, if possible. + /// If a canonical class can not be found, returns the original class. + Iterable _findCanonicalFor( + Iterable containers) { + return containers.map((container) { + var canonical = packageGraph.findCanonicalModelElementFor(container); + return canonical as InheritingContainer? ?? container; + }); + } + bool get hasPublicInterfaces => publicInterfaces.isNotEmpty; List get interfaceElements => [ diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index 8d495ea101..58d557ca7c 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -439,7 +439,7 @@ abstract class ModelElement ExtensionType() => enclosingElement, _ => null, }; - return packageGraph.findCanonicalModelElementFor(element, + return packageGraph.findCanonicalModelElementFor(this, preferredClass: preferredClass); }(); @@ -447,9 +447,15 @@ abstract class ModelElement late final String sourceHref = SourceLinker.fromElement(this).href(); + /// The library in which [element] is declared. + /// + /// This is different from [library] if this [ModelElement] is derived from + /// a library that exports [element] from another library. Library get definingLibrary => getModelForElement(element.library!) as Library; + /// A public, documented library which exports this [ModelElement], ideally in + /// [definingLibrary]'s package. late final Library? canonicalLibrary = () { if (element.hasPrivateName) { // Privately named elements can never have a canonical library. @@ -480,6 +486,8 @@ abstract class ModelElement return null; }(); + /// Searches [PackageGraph.libraryExports] for a public, documented library + /// which exports this [ModelElement], ideally in [definingLibrary]'s package. Library? _searchForCanonicalLibrary() { var thisAndExported = packageGraph.libraryExports[definingLibrary.element]; if (thisAndExported == null) { @@ -494,17 +502,20 @@ abstract class ModelElement topLevelElement.enclosingElement != null) { topLevelElement = topLevelElement.enclosingElement!; } + var topLevelElementName = topLevelElement.name; + if (topLevelElementName == null) { + // Any member of an unnamed extension is not public, and has no + // canonical library. + return null; + } final candidateLibraries = thisAndExported .where((l) => l.isPublic && l.package.documentedWhere != DocumentLocation.missing) .where((l) { - var lookup = - l.element.exportNamespace.definedNames[topLevelElement.name!]; - return switch (lookup) { - PropertyAccessorElement() => topLevelElement == lookup.variable2, - _ => topLevelElement == lookup, - }; + var lookup = l.element.exportNamespace.definedNames[topLevelElementName]; + return topLevelElement == + (lookup is PropertyAccessorElement ? lookup.variable2 : lookup); }).toList(growable: true); // Avoid claiming canonicalization for elements outside of this element's @@ -612,8 +623,8 @@ abstract class ModelElement bool get hasParameters => parameters.isNotEmpty; - /// If [canonicalLibrary] (or [canonicalEnclosingElement], for [Inheritable] - /// subclasses) is null, this is null. + /// If [canonicalLibrary] (or [Inheritable.canonicalEnclosingContainer], for + /// [Inheritable] subclasses) is `null`, this is `null`. @override String? get href { if (!identical(canonicalModelElement, this)) { diff --git a/lib/src/model/package_graph.dart b/lib/src/model/package_graph.dart index f76ac13247..c37635b296 100644 --- a/lib/src/model/package_graph.dart +++ b/lib/src/model/package_graph.dart @@ -708,121 +708,108 @@ class PackageGraph with CommentReferable, Nameable { return buffer.toString(); } - final Map _canonicalLibraryFor = {}; - - /// Tries to find a top level library that references this element. - Library? _findCanonicalLibraryFor(Element e) { - assert(allLibrariesAdded); - if (_canonicalLibraryFor.containsKey(e)) { - return _canonicalLibraryFor[e]; - } - _canonicalLibraryFor[e] = null; - - var searchElement = switch (e) { - PropertyAccessorElement() => e.variable2, - GenericFunctionTypeElement() => e.enclosingElement, - _ => e, - }; - if (searchElement == null) return null; - for (var library in publicLibraries) { - var modelElements = library.modelElementsMap[searchElement]; - if (modelElements != null) { - if (modelElements.any((element) => element.isCanonical)) { - return _canonicalLibraryFor[e] = library; - } - } - } - return _canonicalLibraryFor[e]; - } - - /// Tries to find a canonical [ModelElement] for this [e]. If we know this - /// element is related to a particular class, pass a [preferredClass] to - /// disambiguate. + /// Tries to find a canonical [ModelElement] for this [modelElement]. If we + /// know this element is related to a particular class, pass a + /// [preferredClass] to disambiguate. /// /// This doesn't know anything about [PackageGraph.inheritThrough] and /// probably shouldn't, so using it with [Inheritable]s without special casing /// is not advised. - ModelElement? findCanonicalModelElementFor(Element? e, + /// + /// This can return `null` in a few ways: if [modelElement] is `null`, or if + /// it has no canonical library, or if a possible canonical model element has + /// a `false` value for `isCanonical`. + ModelElement? findCanonicalModelElementFor(ModelElement? modelElement, {Container? preferredClass}) { assert(allLibrariesAdded); - if (e == null) return null; + if (modelElement == null) return null; + var element = modelElement.element; if (preferredClass != null) { var canonicalClass = - findCanonicalModelElementFor(preferredClass.element) as Container?; + findCanonicalModelElementFor(preferredClass) as Container?; if (canonicalClass != null) preferredClass = canonicalClass; } - var lib = _findCanonicalLibraryFor(e); + var lib = modelElement.canonicalLibrary; if (lib == null && preferredClass != null) { - lib = _findCanonicalLibraryFor(preferredClass.element); + lib = preferredClass.canonicalLibrary; } // For elements defined in extensions, they are canonical. - var enclosingElement = e.enclosingElement; + var enclosingElement = element.enclosingElement; if (enclosingElement is ExtensionElement) { lib ??= getModelForElement(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 != null && lib.isDocumented) { - return getModelFor(e, lib); + return getModelFor(element, lib); } } // TODO(jcollins-g): The data structures should be changed to eliminate // guesswork with member elements. - var declaration = e.declaration; - ModelElement? modelElement; + var declaration = element.declaration; + ModelElement? canonicalModelElement; if (declaration != null && - (e is ClassMemberElement || e is PropertyAccessorElement)) { - e = declaration; - modelElement = _findCanonicalModelElementForAmbiguous(e, lib, + (element is ClassMemberElement || element is PropertyAccessorElement)) { + modelElement = getModelForElement(declaration); + element = modelElement.element; + canonicalModelElement = _findCanonicalModelElementForAmbiguous( + modelElement, lib, preferredClass: preferredClass as InheritingContainer?); } else { if (lib != null) { - if (e is PropertyInducingElement) { - var getter = e.getter != null ? getModelFor(e.getter!, lib) : null; - var setter = e.setter != null ? getModelFor(e.setter!, lib) : null; - modelElement = getModelForPropertyInducingElement(e, lib, + if (element is PropertyInducingElement) { + var getter = + element.getter != null ? getModelFor(element.getter!, lib) : null; + var setter = + element.setter != null ? getModelFor(element.setter!, lib) : null; + canonicalModelElement = getModelForPropertyInducingElement( + element, lib, getter: getter as Accessor?, setter: setter as Accessor?); } else { - modelElement = getModelFor(e, lib); + canonicalModelElement = getModelFor(element, lib); } } - assert(modelElement is! Inheritable); - if (modelElement != null && !modelElement.isCanonical) { - modelElement = null; + assert(canonicalModelElement is! Inheritable); + if (canonicalModelElement != null && !canonicalModelElement.isCanonical) { + canonicalModelElement = null; } } // Prefer fields and top-level variables. - if (e is PropertyAccessorElement && modelElement is Accessor) { - modelElement = modelElement.enclosingCombo; + if (element is PropertyAccessorElement && + canonicalModelElement is Accessor) { + canonicalModelElement = canonicalModelElement.enclosingCombo; } - return modelElement; + return canonicalModelElement; } - ModelElement? _findCanonicalModelElementForAmbiguous(Element e, Library? lib, + ModelElement? _findCanonicalModelElementForAmbiguous( + ModelElement modelElement, Library? lib, {InheritingContainer? preferredClass}) { + var element = modelElement.element; var candidates = {}; if (lib != null) { var constructedWithKey = allConstructedModelElements[ - ConstructedModelElementsKey(e, lib, null)]; + ConstructedModelElementsKey(element, lib, null)]; if (constructedWithKey != null) { candidates.add(constructedWithKey); } var constructedWithKeyWithClass = allConstructedModelElements[ - ConstructedModelElementsKey(e, lib, preferredClass)]; + ConstructedModelElementsKey(element, lib, preferredClass)]; if (constructedWithKeyWithClass != null) { candidates.add(constructedWithKeyWithClass); } if (candidates.isEmpty) { candidates = { - ...?allInheritableElements[InheritableElementsKey(e, lib)] + ...?allInheritableElements[InheritableElementsKey(element, lib)] ?.where((me) => me.isCanonical), }; } } - var canonicalClass = findCanonicalModelElementFor(e.enclosingElement); + var canonicalClass = + findCanonicalModelElementFor(modelElement.enclosingElement); if (canonicalClass is InheritingContainer) { candidates.addAll(canonicalClass.allCanonicalModelElements - .where((m) => m.element == e)); + .where((m) => m.element == element)); } var matches = {...candidates.where((me) => me.isCanonical)}; diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 9cad5ff49e..23f6ffd98b 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -52,16 +52,6 @@ Iterable filterHasCanonical( return maybeHasCanonicalItems.where((me) => me.canonicalModelElement != null); } -/// Finds canonical classes for all classes in the iterable, if possible. -/// If a canonical class can not be found, returns the original class. -Iterable findCanonicalFor( - Iterable containers) { - return containers.map((c) => - c.packageGraph.findCanonicalModelElementFor(c.element) - as InheritingContainer? ?? - c); -} - extension ElementExtension on Element { bool get hasPrivateName { final name = this.name;