55import 'package:analyzer/dart/element/element.dart' ;
66import 'package:collection/collection.dart' show IterableExtension;
77import 'package:dartdoc/src/model/attribute.dart' ;
8+ import 'package:dartdoc/src/model/comment_referable.dart' ;
89import 'package:dartdoc/src/model/model.dart' ;
9- import 'package:dartdoc/src/special_elements.dart' ;
1010
1111/// Mixin for subclasses of [ModelElement] representing elements that can be
1212/// inherited from one class to another.
@@ -43,6 +43,9 @@ mixin Inheritable on ContainerMember {
4343 canonicalEnclosingContainer? .canonicalLibrary;
4444
4545 @override
46+ // TODO(srawlins): Do something about this overridden field. Maybe split out
47+ // the super implementation.
48+ // ignore: overridden_fields
4649 late final ModelElement ? canonicalModelElement = canonicalEnclosingContainer
4750 ? .allCanonicalModelElements
4851 .firstWhereOrNull ((m) =>
@@ -55,22 +58,40 @@ mixin Inheritable on ContainerMember {
5558 var searchElement = element.declaration;
5659 // TODO(jcollins-g): generate warning if an inherited element's definition
5760 // is in an intermediate non-canonical class in the inheritance chain?
58- Container ? previous;
59- Container ? previousNonSkippable;
6061 Container ? found;
61- for (var c in _inheritance.reversed) {
62- // Filter out mixins.
63- if (c.containsElement (searchElement)) {
64- if ((packageGraph.inheritThrough.contains (previous) &&
65- c != definingEnclosingContainer) ||
66- (packageGraph.inheritThrough.contains (c) &&
67- c == definingEnclosingContainer)) {
68- return previousNonSkippable!
69- .memberByExample (this )
70- .canonicalEnclosingContainer;
62+ var reverseInheritance = _inheritance.reversed.toList ();
63+ for (var i = 0 ; i < reverseInheritance.length; i++ ) {
64+ var container = reverseInheritance[i];
65+ if (container.containsElement (searchElement)) {
66+ var previousIsHiddenAndNotDefining = i > 0 &&
67+ _isHiddenInterface (reverseInheritance[i - 1 ]) &&
68+ container != definingEnclosingContainer;
69+ var thisIsHiddenAndDefining = _isHiddenInterface (container) &&
70+ container == definingEnclosingContainer;
71+ // If the previous container in the search is one of the "hidden"
72+ // interfaces, and it's not this member's defining container, OR if
73+ // this container in the search is one of the "hidden" interfaces,
74+ // and it is also this member's defining container, then we can just
75+ // immediately return the canonical enclosing container of the
76+ // overridden member in the previous, non-hidden container in the
77+ // inheritance.
78+ if (previousIsHiddenAndNotDefining || thisIsHiddenAndDefining) {
79+ var previousVisible = reverseInheritance
80+ .take (i)
81+ .lastWhere ((e) => ! _isHiddenInterface (e));
82+ var membersInPreviousVisible = previousVisible.allModelElements
83+ .where ((e) => e.name == name)
84+ .whereType <Inheritable >()
85+ .whereNotType <Field >();
86+ assert (
87+ membersInPreviousVisible.length == 1 ,
88+ 'found multiple members named "$name " in '
89+ '"${previousVisible .name }": '
90+ '${membersInPreviousVisible .toList ()}' );
91+ return membersInPreviousVisible.first.canonicalEnclosingContainer;
7192 }
72- var canonicalContainer =
73- packageGraph .findCanonicalModelElementFor (c ) as Container ? ;
93+ var canonicalContainer = packageGraph
94+ .findCanonicalModelElementFor (container ) as Container ? ;
7495 // TODO(jcollins-g): invert this lookup so traversal is recursive
7596 // starting from the ModelElement.
7697 if (canonicalContainer != null ) {
@@ -80,10 +101,6 @@ mixin Inheritable on ContainerMember {
80101 break ;
81102 }
82103 }
83- previous = c;
84- if (! packageGraph.inheritThrough.contains (c)) {
85- previousNonSkippable = c;
86- }
87104 }
88105 if (found != null ) {
89106 return found;
@@ -96,6 +113,20 @@ mixin Inheritable on ContainerMember {
96113 return super .computeCanonicalEnclosingContainer ();
97114 }
98115
116+ /// Whether [c] is a "hidden" interface.
117+ ///
118+ /// A hidden interface should never be considered the canonical enclosing
119+ /// container of a container member.
120+ ///
121+ /// Add classes here if they are similar to the Dart SDK's 'Interceptor' class
122+ /// in that they are to be ignored even when they are the implementers of
123+ /// [Inheritable] s, and the class these inherit from should instead claim
124+ /// implementation.
125+ bool _isHiddenInterface (Container ? c) =>
126+ c != null &&
127+ c.element.name == 'Interceptor' &&
128+ c.element.library? .name == '_interceptors' ;
129+
99130 /// A roughly ordered list of this element's enclosing container's inheritance
100131 /// chain.
101132 ///
@@ -104,10 +135,9 @@ mixin Inheritable on ContainerMember {
104135 var inheritance = [
105136 ...(enclosingElement as InheritingContainer ).inheritanceChain,
106137 ];
107- var object = packageGraph.specialClasses[SpecialClass .object]! ;
108138
109139 assert (
110- definingEnclosingContainer == object ||
140+ definingEnclosingContainer.isDartCoreObject ||
111141 inheritance.contains (definingEnclosingContainer), () {
112142 var inheritanceDescriptions = inheritance
113143 .map ((e) =>
@@ -121,8 +151,8 @@ mixin Inheritable on ContainerMember {
121151 }());
122152 // Unless the code explicitly extends dart:core's Object, we won't get
123153 // an entry here. So add it.
124- if (inheritance.last != object ) {
125- inheritance.add (object );
154+ if (! inheritance.last.isDartCoreObject ) {
155+ inheritance.add (packageGraph.objectClass );
126156 }
127157 return inheritance;
128158 }
0 commit comments