Skip to content

Commit b947413

Browse files
authored
Display inherited elements for extension types (#3556)
1 parent c224112 commit b947413

7 files changed

+104
-117
lines changed

lib/src/generator/templates.runtime_renderers.dart

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5394,18 +5394,7 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
53945394
() => {
53955395
..._Renderer_InheritingContainer.propertyMap<CT_>(),
53965396
..._Renderer_Constructable.propertyMap<CT_>(),
5397-
'allFields': Property(
5398-
getValue: (CT_ c) => c.allFields,
5399-
renderVariable: (CT_ c, Property<CT_> self,
5400-
List<String> remainingNames) =>
5401-
self.renderSimpleVariable(
5402-
c, remainingNames, 'List<Field>'),
5403-
renderIterable: (CT_ c, RendererBase<CT_> r,
5404-
List<MustachioNode> ast, StringSink sink) {
5405-
return c.allFields.map((e) =>
5406-
_render_Field(e, ast, r.template, sink, parent: r));
5407-
},
5408-
),
5397+
..._Renderer_TypeImplementing.propertyMap<CT_>(),
54095398
'allModelElements': Property(
54105399
getValue: (CT_ c) => c.allModelElements,
54115400
renderVariable: (CT_ c, Property<CT_> self,
@@ -5489,13 +5478,6 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
54895478
parent: r);
54905479
},
54915480
),
5492-
'hasPublicInterfaces': Property(
5493-
getValue: (CT_ c) => c.hasPublicInterfaces,
5494-
renderVariable: (CT_ c, Property<CT_> self,
5495-
List<String> remainingNames) =>
5496-
self.renderSimpleVariable(c, remainingNames, 'bool'),
5497-
getBool: (CT_ c) => c.hasPublicInterfaces == true,
5498-
),
54995481
'inheritanceChain': Property(
55005482
getValue: (CT_ c) => c.inheritanceChain,
55015483
renderVariable: (CT_ c, Property<CT_> self,
@@ -5509,30 +5491,6 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
55095491
parent: r));
55105492
},
55115493
),
5512-
'inheritedMethods': Property(
5513-
getValue: (CT_ c) => c.inheritedMethods,
5514-
renderVariable: (CT_ c, Property<CT_> self,
5515-
List<String> remainingNames) =>
5516-
self.renderSimpleVariable(
5517-
c, remainingNames, 'Iterable<Method>'),
5518-
renderIterable: (CT_ c, RendererBase<CT_> r,
5519-
List<MustachioNode> ast, StringSink sink) {
5520-
return c.inheritedMethods.map((e) =>
5521-
_render_Method(e, ast, r.template, sink, parent: r));
5522-
},
5523-
),
5524-
'inheritedOperators': Property(
5525-
getValue: (CT_ c) => c.inheritedOperators,
5526-
renderVariable: (CT_ c, Property<CT_> self,
5527-
List<String> remainingNames) =>
5528-
self.renderSimpleVariable(
5529-
c, remainingNames, 'List<Operator>'),
5530-
renderIterable: (CT_ c, RendererBase<CT_> r,
5531-
List<MustachioNode> ast, StringSink sink) {
5532-
return c.inheritedOperators.map((e) =>
5533-
_render_Operator(e, ast, r.template, sink, parent: r));
5534-
},
5535-
),
55365494
'isAbstract': Property(
55375495
getValue: (CT_ c) => c.isAbstract,
55385496
renderVariable: (CT_ c, Property<CT_> self,
@@ -5580,19 +5538,6 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
55805538
parent: r, getters: _invisibleGetters['Kind']!);
55815539
},
55825540
),
5583-
'publicInterfaces': Property(
5584-
getValue: (CT_ c) => c.publicInterfaces,
5585-
renderVariable: (CT_ c, Property<CT_> self,
5586-
List<String> remainingNames) =>
5587-
self.renderSimpleVariable(
5588-
c, remainingNames, 'List<DefinedElementType>'),
5589-
renderIterable: (CT_ c, RendererBase<CT_> r,
5590-
List<MustachioNode> ast, StringSink sink) {
5591-
return c.publicInterfaces.map((e) =>
5592-
_render_DefinedElementType(e, ast, r.template, sink,
5593-
parent: r));
5594-
},
5595-
),
55965541
'referenceChildren': Property(
55975542
getValue: (CT_ c) => c.referenceChildren,
55985543
renderVariable: (CT_ c, Property<CT_> self,
@@ -17003,6 +16948,7 @@ const _invisibleGetters = {
1700316948
'hashCode',
1700416949
'implementors',
1700516950
'inheritThrough',
16951+
'inheritanceManager',
1700616952
'invisibleAnnotations',
1700716953
'libraries',
1700816954
'libraryCount',

lib/src/model/container_member.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ mixin ContainerMember on ModelElement implements EnclosedElement {
3838

3939
Container? computeCanonicalEnclosingContainer() {
4040
final enclosingElement = this.enclosingElement;
41-
if (enclosingElement is! Extension) {
42-
return packageGraph.findCanonicalModelElementFor(element.enclosingElement)
43-
as Container?;
44-
}
45-
// TODO(jcollins-g): move Extension specific code to [Extendable]
46-
return packageGraph.findCanonicalModelElementFor(enclosingElement.element)
47-
as Container?;
41+
return switch (enclosingElement) {
42+
Extension() =>
43+
packageGraph.findCanonicalModelElementFor(enclosingElement.element),
44+
ExtensionType() =>
45+
packageGraph.findCanonicalModelElementFor(enclosingElement.element),
46+
_ => packageGraph.findCanonicalModelElementFor(element.enclosingElement),
47+
} as Container?;
4848
}
4949

5050
@override

lib/src/model/extension_type.dart

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import 'package:dartdoc/src/model/comment_referable.dart';
88
import 'package:dartdoc/src/model/model.dart';
99
import 'package:meta/meta.dart';
1010

11-
class ExtensionType extends InheritingContainer with Constructable {
11+
class ExtensionType extends InheritingContainer
12+
with Constructable, TypeImplementing {
1213
@override
1314
final ExtensionTypeElement element;
1415

@@ -38,25 +39,6 @@ class ExtensionType extends InheritingContainer with Constructable {
3839
@override
3940
bool get isSealed => false;
4041

41-
bool get hasPublicInterfaces => publicInterfaces.isNotEmpty;
42-
43-
@override
44-
List<DefinedElementType> get publicInterfaces {
45-
var interfaces = <DefinedElementType>[];
46-
for (var interface in element.interfaces) {
47-
var elementType =
48-
modelBuilder.typeFrom(interface, library) as DefinedElementType;
49-
50-
if (elementType.modelElement.canonicalModelElement != null) {
51-
interfaces.add(elementType);
52-
continue;
53-
}
54-
55-
// TODO(srawlins): Work through intermediate, private, interfaces.
56-
}
57-
return interfaces;
58-
}
59-
6042
@override
6143
late final List<Field> declaredFields = element.fields.map((field) {
6244
Accessor? getter, setter;
@@ -79,20 +61,10 @@ class ExtensionType extends InheritingContainer with Constructable {
7961
];
8062

8163
@override
82-
// TODO(srawlins): Implement.
83-
List<Field> get allFields => [];
84-
85-
@override
86-
// TODO(srawlins): Implement.
87-
Iterable<Method> get inheritedMethods => [];
88-
89-
@override
90-
// TODO(srawlins): Implement.
91-
List<Operator> get inheritedOperators => [];
92-
93-
@override
94-
// TODO(srawlins): Implement. It might just be empty...
95-
List<InheritingContainer> get inheritanceChain => [];
64+
late final List<InheritingContainer> inheritanceChain = [
65+
this,
66+
...interfaces.expandInheritanceChain,
67+
];
9668

9769
@override
9870
String get filePath => '${library.dirName}/${fileStructure.fileName}';

lib/src/model/inheritable.dart

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@ import 'package:dartdoc/src/model/attribute.dart';
88
import 'package:dartdoc/src/model/model.dart';
99
import 'package:dartdoc/src/special_elements.dart';
1010

11-
/// Mixin for subclasses of ModelElement representing Elements that can be
11+
/// Mixin for subclasses of [ModelElement] representing elements that can be
1212
/// inherited from one class to another.
1313
///
1414
/// We can search the inheritance chain between this instance and
1515
/// [definingEnclosingContainer] in [Inheritable.canonicalEnclosingContainer],
1616
/// for the canonical [Class] closest to where this member was defined. We
17-
/// can then know that when we find [Inheritable.modelElement] inside that [Class]'s
18-
/// namespace, that's the one we should treat as canonical and implementors
19-
/// of this class can use that knowledge to determine canonicalization.
17+
/// can then know that when we find [Inheritable.modelElement] inside that
18+
/// [Class]'s namespace, that's the one we should treat as canonical and
19+
/// implementors of this class can use that knowledge to determine
20+
/// canonicalization.
2021
///
2122
/// We pick the class closest to the [definingEnclosingContainer] so that all
2223
/// children of that class inheriting the same member will point to the same
@@ -89,7 +90,10 @@ mixin Inheritable on ContainerMember {
8990
// classes.
9091
if (definingEnclosingContainer.isCanonical &&
9192
definingEnclosingContainer.isPublic) {
92-
assert(definingEnclosingContainer == found);
93+
assert(
94+
definingEnclosingContainer == found,
95+
'For $element, expected $definingEnclosingContainer to be Object '
96+
'or $found, but was neither.');
9397
}
9498
if (found != null) {
9599
return found;
@@ -106,13 +110,13 @@ mixin Inheritable on ContainerMember {
106110
var inheritance = [
107111
...(enclosingElement as InheritingContainer).inheritanceChain,
108112
];
109-
var object = packageGraph.specialClasses[SpecialClass.object];
113+
var object = packageGraph.specialClasses[SpecialClass.object]!;
114+
110115
assert(definingEnclosingContainer == object ||
111116
inheritance.contains(definingEnclosingContainer));
112-
113-
// Unless the code explicitly extends dart-core's Object, we won't get
117+
// Unless the code explicitly extends dart:core's Object, we won't get
114118
// an entry here. So add it.
115-
if (inheritance.last != object && object != null) {
119+
if (inheritance.last != object) {
116120
inheritance.add(object);
117121
}
118122
assert(inheritance.where((e) => e == object).length == 1);

lib/src/model/inheriting_container.dart

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
import 'package:analyzer/dart/element/element.dart';
66
import 'package:analyzer/dart/element/type.dart';
7-
// ignore: implementation_imports
8-
import 'package:analyzer/src/dart/element/inheritance_manager3.dart'
9-
show InheritanceManager3;
107
import 'package:collection/collection.dart' show IterableExtension;
118
import 'package:dartdoc/src/element_type.dart';
129
import 'package:dartdoc/src/model/comment_referable.dart';
@@ -154,8 +151,9 @@ abstract class InheritingContainer extends Container
154151
}
155152

156153
final concreteInheritenceMap =
157-
_inheritanceManager.getInheritedConcreteMap2(element);
158-
final inheritenceMap = _inheritanceManager.getInheritedMap2(element);
154+
packageGraph.inheritanceManager.getInheritedConcreteMap2(element);
155+
final inheritenceMap =
156+
packageGraph.inheritanceManager.getInheritedMap2(element);
159157

160158
List<InterfaceElement>? inheritanceChainElements;
161159

@@ -195,8 +193,6 @@ abstract class InheritingContainer extends Container
195193
return combinedMap.values.toList(growable: false);
196194
}
197195

198-
static final InheritanceManager3 _inheritanceManager = InheritanceManager3();
199-
200196
/// All fields defined on this container, _including inherited fields_.
201197
List<Field> get allFields {
202198
var inheritedAccessorElements = {

lib/src/model/package_graph.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import 'package:analyzer/dart/ast/ast.dart';
88
import 'package:analyzer/dart/element/element.dart';
99
import 'package:analyzer/file_system/file_system.dart';
1010
// ignore: implementation_imports
11+
import 'package:analyzer/src/dart/element/inheritance_manager3.dart'
12+
show InheritanceManager3;
13+
// ignore: implementation_imports
1114
import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary;
1215
// ignore: implementation_imports
1316
import 'package:analyzer/src/generated/source.dart' show Source;
@@ -43,6 +46,8 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
4346
Package.fromPackageMeta(packageMeta, this);
4447
}
4548

49+
final InheritanceManager3 inheritanceManager = InheritanceManager3();
50+
4651
void dispose() {
4752
// Clear out any cached tool snapshots and temporary directories.
4853
// TODO(jcollins-g): Consider ownership change for these objects

test/templates/extension_type_test.dart

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,21 @@ analyzer:
7373
''',
7474
libFiles: [
7575
d.file('lib.dart', '''
76-
class Base1<E> {}
76+
class Base1<E> {
77+
// An inherited method.
78+
void m2() {}
79+
80+
// An inherited field.
81+
int get field1 => 1;
82+
}
7783
7884
class Base2 {}
7985
8086
class Foo<E> extends Base1<E>, Base2 {}
8187
8288
class FooSub extends Foo<int> {}
8389
84-
extension type One<E>(Foo<E> e) {
90+
extension type One<E>(Foo<E> it) {
8591
/// A named constructor.
8692
MyIterable.named(Foo<E> e);
8793
@@ -109,7 +115,7 @@ extension type One<E>(Foo<E> e) {
109115
110116
extension type TwoWithBase<E>(Foo<E> it) implements Base1<E>, Base2 {}
111117
112-
extension type ThreeWithOne<E>(FooSub it) implements One<int> {}
118+
extension type ThreeWithOne<E>(FooSub it3) implements One<int> {}
113119
'''),
114120
],
115121
dartdocOptions: '''
@@ -216,8 +222,66 @@ dartdoc:
216222
},
217223
);
218224

219-
test('page contains instance operators', () async {
225+
test('page contains representation field', () async {
226+
oneLines.expectMainContentContainsAllInOrder([
227+
matches('<h2>Properties</h2>'),
228+
matches('<a href="../lib/One/it.html">it</a>'),
229+
matches('An operator.'),
230+
]);
231+
});
232+
233+
test('page contains inherited representation field', () async {
234+
threeLines.expectMainContentContainsAllInOrder([
235+
matches('<h2>Properties</h2>'),
236+
matches('<a href="../lib/One/it.html">it</a>'),
237+
]);
238+
});
239+
240+
test('page contains inherited getters', () async {
241+
twoLines.expectMainContentContainsAllInOrder([
242+
matches('<h2>Properties</h2>'),
243+
matches('<a href="../lib/Base1/field1.html">field1</a>'),
244+
]);
245+
});
246+
247+
test('page contains instance methods', () async {
220248
oneLines.expectMainContentContainsAllInOrder([
249+
matches('<h2>Methods</h2>'),
250+
matches('<dt id="m1" class="callable">'),
251+
matches('<a href="../lib/One/m1.html">m1</a>'),
252+
matches('An instance method.'),
253+
]);
254+
});
255+
256+
test('page contains methods inherited from a class super-interface',
257+
() async {
258+
twoLines.expectMainContentContainsAllInOrder([
259+
matches('<h2>Methods</h2>'),
260+
matches('<dt id="m2" class="callable inherited">'),
261+
matches('<a href="../lib/Base1/m2.html">m2</a>'),
262+
]);
263+
});
264+
265+
test(
266+
'page contains methods inherited from an extension type '
267+
'super-interface', () async {
268+
threeLines.expectMainContentContainsAllInOrder([
269+
matches('<h2>Methods</h2>'),
270+
matches('<dt id="m1" class="callable inherited">'),
271+
matches('<a href="../lib/One/m1.html">m1</a>'),
272+
]);
273+
});
274+
275+
test('page contains operators', () async {
276+
oneLines.expectMainContentContainsAllInOrder([
277+
matches('<h2>Operators</h2>'),
278+
matches('<a href="../lib/One/operator_greater.html">operator ></a>'),
279+
matches('An operator.'),
280+
]);
281+
});
282+
283+
test('page contains inherited operators', () async {
284+
threeLines.expectMainContentContainsAllInOrder([
221285
matches('<h2>Operators</h2>'),
222286
matches('<a href="../lib/One/operator_greater.html">operator ></a>'),
223287
matches('An operator.'),

0 commit comments

Comments
 (0)