Skip to content

Commit

Permalink
Display inherited elements for extension types (#3556)
Browse files Browse the repository at this point in the history
  • Loading branch information
srawlins committed Oct 30, 2023
1 parent c224112 commit b947413
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 117 deletions.
58 changes: 2 additions & 56 deletions lib/src/generator/templates.runtime_renderers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5394,18 +5394,7 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
() => {
..._Renderer_InheritingContainer.propertyMap<CT_>(),
..._Renderer_Constructable.propertyMap<CT_>(),
'allFields': Property(
getValue: (CT_ c) => c.allFields,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'List<Field>'),
renderIterable: (CT_ c, RendererBase<CT_> r,
List<MustachioNode> ast, StringSink sink) {
return c.allFields.map((e) =>
_render_Field(e, ast, r.template, sink, parent: r));
},
),
..._Renderer_TypeImplementing.propertyMap<CT_>(),
'allModelElements': Property(
getValue: (CT_ c) => c.allModelElements,
renderVariable: (CT_ c, Property<CT_> self,
Expand Down Expand Up @@ -5489,13 +5478,6 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
parent: r);
},
),
'hasPublicInterfaces': Property(
getValue: (CT_ c) => c.hasPublicInterfaces,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(c, remainingNames, 'bool'),
getBool: (CT_ c) => c.hasPublicInterfaces == true,
),
'inheritanceChain': Property(
getValue: (CT_ c) => c.inheritanceChain,
renderVariable: (CT_ c, Property<CT_> self,
Expand All @@ -5509,30 +5491,6 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
parent: r));
},
),
'inheritedMethods': Property(
getValue: (CT_ c) => c.inheritedMethods,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'Iterable<Method>'),
renderIterable: (CT_ c, RendererBase<CT_> r,
List<MustachioNode> ast, StringSink sink) {
return c.inheritedMethods.map((e) =>
_render_Method(e, ast, r.template, sink, parent: r));
},
),
'inheritedOperators': Property(
getValue: (CT_ c) => c.inheritedOperators,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'List<Operator>'),
renderIterable: (CT_ c, RendererBase<CT_> r,
List<MustachioNode> ast, StringSink sink) {
return c.inheritedOperators.map((e) =>
_render_Operator(e, ast, r.template, sink, parent: r));
},
),
'isAbstract': Property(
getValue: (CT_ c) => c.isAbstract,
renderVariable: (CT_ c, Property<CT_> self,
Expand Down Expand Up @@ -5580,19 +5538,6 @@ class _Renderer_ExtensionType extends RendererBase<ExtensionType> {
parent: r, getters: _invisibleGetters['Kind']!);
},
),
'publicInterfaces': Property(
getValue: (CT_ c) => c.publicInterfaces,
renderVariable: (CT_ c, Property<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(
c, remainingNames, 'List<DefinedElementType>'),
renderIterable: (CT_ c, RendererBase<CT_> r,
List<MustachioNode> ast, StringSink sink) {
return c.publicInterfaces.map((e) =>
_render_DefinedElementType(e, ast, r.template, sink,
parent: r));
},
),
'referenceChildren': Property(
getValue: (CT_ c) => c.referenceChildren,
renderVariable: (CT_ c, Property<CT_> self,
Expand Down Expand Up @@ -17003,6 +16948,7 @@ const _invisibleGetters = {
'hashCode',
'implementors',
'inheritThrough',
'inheritanceManager',
'invisibleAnnotations',
'libraries',
'libraryCount',
Expand Down
14 changes: 7 additions & 7 deletions lib/src/model/container_member.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ mixin ContainerMember on ModelElement implements EnclosedElement {

Container? computeCanonicalEnclosingContainer() {
final enclosingElement = this.enclosingElement;
if (enclosingElement is! Extension) {
return packageGraph.findCanonicalModelElementFor(element.enclosingElement)
as Container?;
}
// TODO(jcollins-g): move Extension specific code to [Extendable]
return packageGraph.findCanonicalModelElementFor(enclosingElement.element)
as Container?;
return switch (enclosingElement) {
Extension() =>
packageGraph.findCanonicalModelElementFor(enclosingElement.element),
ExtensionType() =>
packageGraph.findCanonicalModelElementFor(enclosingElement.element),
_ => packageGraph.findCanonicalModelElementFor(element.enclosingElement),
} as Container?;
}

@override
Expand Down
40 changes: 6 additions & 34 deletions lib/src/model/extension_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import 'package:dartdoc/src/model/comment_referable.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:meta/meta.dart';

class ExtensionType extends InheritingContainer with Constructable {
class ExtensionType extends InheritingContainer
with Constructable, TypeImplementing {
@override
final ExtensionTypeElement element;

Expand Down Expand Up @@ -38,25 +39,6 @@ class ExtensionType extends InheritingContainer with Constructable {
@override
bool get isSealed => false;

bool get hasPublicInterfaces => publicInterfaces.isNotEmpty;

@override
List<DefinedElementType> get publicInterfaces {
var interfaces = <DefinedElementType>[];
for (var interface in element.interfaces) {
var elementType =
modelBuilder.typeFrom(interface, library) as DefinedElementType;

if (elementType.modelElement.canonicalModelElement != null) {
interfaces.add(elementType);
continue;
}

// TODO(srawlins): Work through intermediate, private, interfaces.
}
return interfaces;
}

@override
late final List<Field> declaredFields = element.fields.map((field) {
Accessor? getter, setter;
Expand All @@ -79,20 +61,10 @@ class ExtensionType extends InheritingContainer with Constructable {
];

@override
// TODO(srawlins): Implement.
List<Field> get allFields => [];

@override
// TODO(srawlins): Implement.
Iterable<Method> get inheritedMethods => [];

@override
// TODO(srawlins): Implement.
List<Operator> get inheritedOperators => [];

@override
// TODO(srawlins): Implement. It might just be empty...
List<InheritingContainer> get inheritanceChain => [];
late final List<InheritingContainer> inheritanceChain = [
this,
...interfaces.expandInheritanceChain,
];

@override
String get filePath => '${library.dirName}/${fileStructure.fileName}';
Expand Down
22 changes: 13 additions & 9 deletions lib/src/model/inheritable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import 'package:dartdoc/src/model/attribute.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/special_elements.dart';

/// Mixin for subclasses of ModelElement representing Elements that can be
/// Mixin for subclasses of [ModelElement] representing elements that can be
/// inherited from one class to another.
///
/// We can search the inheritance chain between this instance and
/// [definingEnclosingContainer] in [Inheritable.canonicalEnclosingContainer],
/// for the canonical [Class] closest to where this member was defined. We
/// can then know that when we find [Inheritable.modelElement] inside that [Class]'s
/// namespace, that's the one we should treat as canonical and implementors
/// of this class can use that knowledge to determine canonicalization.
/// can then know that when we find [Inheritable.modelElement] inside that
/// [Class]'s namespace, that's the one we should treat as canonical and
/// implementors of this class can use that knowledge to determine
/// canonicalization.
///
/// We pick the class closest to the [definingEnclosingContainer] so that all
/// children of that class inheriting the same member will point to the same
Expand Down Expand Up @@ -89,7 +90,10 @@ mixin Inheritable on ContainerMember {
// classes.
if (definingEnclosingContainer.isCanonical &&
definingEnclosingContainer.isPublic) {
assert(definingEnclosingContainer == found);
assert(
definingEnclosingContainer == found,
'For $element, expected $definingEnclosingContainer to be Object '
'or $found, but was neither.');
}
if (found != null) {
return found;
Expand All @@ -106,13 +110,13 @@ mixin Inheritable on ContainerMember {
var inheritance = [
...(enclosingElement as InheritingContainer).inheritanceChain,
];
var object = packageGraph.specialClasses[SpecialClass.object];
var object = packageGraph.specialClasses[SpecialClass.object]!;

assert(definingEnclosingContainer == object ||
inheritance.contains(definingEnclosingContainer));

// Unless the code explicitly extends dart-core's Object, we won't get
// Unless the code explicitly extends dart:core's Object, we won't get
// an entry here. So add it.
if (inheritance.last != object && object != null) {
if (inheritance.last != object) {
inheritance.add(object);
}
assert(inheritance.where((e) => e == object).length == 1);
Expand Down
10 changes: 3 additions & 7 deletions lib/src/model/inheriting_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
// ignore: implementation_imports
import 'package:analyzer/src/dart/element/inheritance_manager3.dart'
show InheritanceManager3;
import 'package:collection/collection.dart' show IterableExtension;
import 'package:dartdoc/src/element_type.dart';
import 'package:dartdoc/src/model/comment_referable.dart';
Expand Down Expand Up @@ -154,8 +151,9 @@ abstract class InheritingContainer extends Container
}

final concreteInheritenceMap =
_inheritanceManager.getInheritedConcreteMap2(element);
final inheritenceMap = _inheritanceManager.getInheritedMap2(element);
packageGraph.inheritanceManager.getInheritedConcreteMap2(element);
final inheritenceMap =
packageGraph.inheritanceManager.getInheritedMap2(element);

List<InterfaceElement>? inheritanceChainElements;

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

static final InheritanceManager3 _inheritanceManager = InheritanceManager3();

/// All fields defined on this container, _including inherited fields_.
List<Field> get allFields {
var inheritedAccessorElements = {
Expand Down
5 changes: 5 additions & 0 deletions lib/src/model/package_graph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
// ignore: implementation_imports
import 'package:analyzer/src/dart/element/inheritance_manager3.dart'
show InheritanceManager3;
// ignore: implementation_imports
import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary;
// ignore: implementation_imports
import 'package:analyzer/src/generated/source.dart' show Source;
Expand Down Expand Up @@ -43,6 +46,8 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
Package.fromPackageMeta(packageMeta, this);
}

final InheritanceManager3 inheritanceManager = InheritanceManager3();

void dispose() {
// Clear out any cached tool snapshots and temporary directories.
// TODO(jcollins-g): Consider ownership change for these objects
Expand Down
72 changes: 68 additions & 4 deletions test/templates/extension_type_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,21 @@ analyzer:
''',
libFiles: [
d.file('lib.dart', '''
class Base1<E> {}
class Base1<E> {
// An inherited method.
void m2() {}
// An inherited field.
int get field1 => 1;
}
class Base2 {}
class Foo<E> extends Base1<E>, Base2 {}
class FooSub extends Foo<int> {}
extension type One<E>(Foo<E> e) {
extension type One<E>(Foo<E> it) {
/// A named constructor.
MyIterable.named(Foo<E> e);
Expand Down Expand Up @@ -109,7 +115,7 @@ extension type One<E>(Foo<E> e) {
extension type TwoWithBase<E>(Foo<E> it) implements Base1<E>, Base2 {}
extension type ThreeWithOne<E>(FooSub it) implements One<int> {}
extension type ThreeWithOne<E>(FooSub it3) implements One<int> {}
'''),
],
dartdocOptions: '''
Expand Down Expand Up @@ -216,8 +222,66 @@ dartdoc:
},
);

test('page contains instance operators', () async {
test('page contains representation field', () async {
oneLines.expectMainContentContainsAllInOrder([
matches('<h2>Properties</h2>'),
matches('<a href="../lib/One/it.html">it</a>'),
matches('An operator.'),
]);
});

test('page contains inherited representation field', () async {
threeLines.expectMainContentContainsAllInOrder([
matches('<h2>Properties</h2>'),
matches('<a href="../lib/One/it.html">it</a>'),
]);
});

test('page contains inherited getters', () async {
twoLines.expectMainContentContainsAllInOrder([
matches('<h2>Properties</h2>'),
matches('<a href="../lib/Base1/field1.html">field1</a>'),
]);
});

test('page contains instance methods', () async {
oneLines.expectMainContentContainsAllInOrder([
matches('<h2>Methods</h2>'),
matches('<dt id="m1" class="callable">'),
matches('<a href="../lib/One/m1.html">m1</a>'),
matches('An instance method.'),
]);
});

test('page contains methods inherited from a class super-interface',
() async {
twoLines.expectMainContentContainsAllInOrder([
matches('<h2>Methods</h2>'),
matches('<dt id="m2" class="callable inherited">'),
matches('<a href="../lib/Base1/m2.html">m2</a>'),
]);
});

test(
'page contains methods inherited from an extension type '
'super-interface', () async {
threeLines.expectMainContentContainsAllInOrder([
matches('<h2>Methods</h2>'),
matches('<dt id="m1" class="callable inherited">'),
matches('<a href="../lib/One/m1.html">m1</a>'),
]);
});

test('page contains operators', () async {
oneLines.expectMainContentContainsAllInOrder([
matches('<h2>Operators</h2>'),
matches('<a href="../lib/One/operator_greater.html">operator ></a>'),
matches('An operator.'),
]);
});

test('page contains inherited operators', () async {
threeLines.expectMainContentContainsAllInOrder([
matches('<h2>Operators</h2>'),
matches('<a href="../lib/One/operator_greater.html">operator ></a>'),
matches('An operator.'),
Expand Down

0 comments on commit b947413

Please sign in to comment.