Skip to content

Commit dc56630

Browse files
authored
Refactor "could apply" logic for extensions (#3847)
This logic was implemented in a few places between Extension and ElementType (and subtypes). We don't need any logic to live in ElementTypes though, since any calculations that need to be made regarding DartTypes or InterfaceTypes can be made in Extension (`alwaysApplies` and `couldApplyTo`). This fixes #3846 and dramatically simplifies the implementation. Also: * Rename `Extension.extendedType` to `Extension.extendedElement`. * Change `PackageGraph._extensions` to a Set, to remove duplication in multiple canonical libraries. * Introduce a classes test. * Delete some redundant end2end testing code.
1 parent e233b2b commit dc56630

14 files changed

+264
-181
lines changed

lib/src/element_type.dart

-58
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ library;
1010
import 'package:analyzer/dart/element/element.dart';
1111
import 'package:analyzer/dart/element/nullability_suffix.dart';
1212
import 'package:analyzer/dart/element/type.dart';
13-
import 'package:analyzer/dart/element/type_system.dart';
1413
import 'package:dartdoc/src/model/comment_referable.dart';
1514
import 'package:dartdoc/src/model/model.dart';
1615
import 'package:dartdoc/src/render/element_type_renderer.dart';
@@ -58,13 +57,8 @@ abstract class ElementType with CommentReferable, Nameable {
5857
@override
5958
String get breadcrumbName => throw UnimplementedError();
6059

61-
DartType get instantiatedType;
62-
6360
Iterable<ElementType> get typeArguments;
6461

65-
bool isBoundSupertypeTo(ElementType t);
66-
bool isSubtypeOf(ElementType t);
67-
6862
@override
6963
String toString() => '$type';
7064
}
@@ -112,16 +106,6 @@ class UndefinedElementType extends ElementType {
112106
@override
113107
String get nameWithGenerics => '$name$nullabilitySuffix';
114108

115-
/// Assume that undefined elements don't have useful bounds.
116-
@override
117-
DartType get instantiatedType => type;
118-
119-
@override
120-
bool isBoundSupertypeTo(ElementType t) => false;
121-
122-
@override
123-
bool isSubtypeOf(ElementType t) => type.isBottom && !t.type.isBottom;
124-
125109
@override
126110
String get linkedName => name;
127111

@@ -257,9 +241,6 @@ class TypeParameterElementType extends DefinedElementType {
257241

258242
@override
259243
String get nameWithGenerics => '$name$nullabilitySuffix';
260-
261-
@override
262-
DartType get _bound => type.bound;
263244
}
264245

265246
/// An [ElementType] associated with an [Element].
@@ -313,45 +294,6 @@ abstract class DefinedElementType extends ElementType {
313294
return canonicalClass.isPublic;
314295
}
315296

316-
TypeSystem get _typeSystem => library.element.typeSystem;
317-
318-
DartType get _bound => type;
319-
320-
/// This type, instantiated to bounds if it isn't already.
321-
@override
322-
late final DartType instantiatedType = () {
323-
final bound = _bound;
324-
if (bound is InterfaceType &&
325-
!bound.typeArguments.every((t) => t is InterfaceType)) {
326-
return _typeSystem.instantiateInterfaceToBounds(
327-
element: bound.element, nullabilitySuffix: _bound.nullabilitySuffix);
328-
} else {
329-
return _bound;
330-
}
331-
}();
332-
333-
/// Returns whether the instantiated-to-bounds type of this type is a subtype
334-
/// of [type].
335-
@override
336-
bool isSubtypeOf(ElementType type) =>
337-
_typeSystem.isSubtypeOf(instantiatedType, type.instantiatedType);
338-
339-
/// Whether at least one supertype (including via mixins and interfaces) is
340-
/// equivalent to or a subtype of `this` when instantiated to bounds.
341-
@override
342-
bool isBoundSupertypeTo(ElementType t) {
343-
var type = t.instantiatedType;
344-
if (type is InterfaceType) {
345-
var superTypes = type.allSupertypes;
346-
for (var superType in superTypes) {
347-
if (_typeSystem.isSubtypeOf(superType, instantiatedType)) {
348-
return true;
349-
}
350-
}
351-
}
352-
return false;
353-
}
354-
355297
@override
356298
Iterable<ElementType> get typeArguments => [];
357299

lib/src/generator/templates.aot_renderers_for_html.dart

+8-13
Original file line numberDiff line numberDiff line change
@@ -690,15 +690,10 @@ String renderExtension<T extends Extension>(ExtensionTemplateData<T> context0) {
690690
<dl class="dl-horizontal">
691691
<dt>on</dt>
692692
<dd>
693-
<ul class="comma-separated clazz-relationships">''');
694-
var context3 = context2.extendedType;
695-
buffer.writeln();
696-
buffer.write('''
697-
<li>''');
698-
buffer.write(context3.linkedName);
699-
buffer.write('''</li>''');
700-
buffer.writeln();
701-
buffer.write('''
693+
<ul class="comma-separated clazz-relationships">
694+
<li>''');
695+
buffer.write(context2.extendedElement.linkedName);
696+
buffer.write('''</li>
702697
</ul>
703698
</dd>
704699
</dl>
@@ -720,7 +715,7 @@ String renderExtension<T extends Extension>(ExtensionTemplateData<T> context0) {
720715
buffer.write(_renderExtension_partial_static_methods_10(context2));
721716
buffer.write('\n ');
722717
buffer.write(_renderExtension_partial_static_constants_11(context2));
723-
var context4 = context0.extension;
718+
var context3 = context0.extension;
724719
buffer.writeln();
725720
buffer.write('''
726721
@@ -1397,7 +1392,7 @@ String renderMethod(MethodTemplateData context0) {
13971392
buffer.write(' ');
13981393
buffer.writeEscaped(context0.parent!.kind.toString());
13991394
buffer.write(''' on ''');
1400-
buffer.write(context0.parentAsExtension.extendedType.linkedName);
1395+
buffer.write(context0.parentAsExtension.extendedElement.linkedName);
14011396
buffer.write('''</h5>''');
14021397
}
14031398
if (!context0.isParentExtension) {
@@ -1626,7 +1621,7 @@ String renderProperty(PropertyTemplateData context0) {
16261621
buffer.write(' ');
16271622
buffer.writeEscaped(context0.parent!.kind.toString());
16281623
buffer.write(''' on ''');
1629-
buffer.write(context0.parentAsExtension.extendedType.linkedName);
1624+
buffer.write(context0.parentAsExtension.extendedElement.linkedName);
16301625
buffer.write('''</h5>''');
16311626
}
16321627
if (!context0.isParentExtension) {
@@ -3645,7 +3640,7 @@ String _deduplicated_lib_templates__extension_html(Extension context0) {
36453640
buffer.write(context0.linkedName);
36463641
buffer.write('''</span>
36473642
on ''');
3648-
buffer.write(context0.extendedType.linkedName);
3643+
buffer.write(context0.extendedElement.linkedName);
36493644
buffer.write('\n ');
36503645
buffer.write(
36513646
__deduplicated_lib_templates__extension_html_partial_categorization_0(

lib/src/generator/templates.runtime_renderers.dart

+4-27
Original file line numberDiff line numberDiff line change
@@ -3467,18 +3467,6 @@ class _Renderer_DefinedElementType extends RendererBase<DefinedElementType> {
34673467
parent: r);
34683468
},
34693469
),
3470-
'instantiatedType': Property(
3471-
getValue: (CT_ c) => c.instantiatedType,
3472-
renderVariable: (CT_ c, Property<CT_> self,
3473-
List<String> remainingNames) =>
3474-
self.renderSimpleVariable(c, remainingNames, 'DartType'),
3475-
isNullValue: (CT_ c) => false,
3476-
renderValue: (CT_ c, RendererBase<CT_> r,
3477-
List<MustachioNode> ast, StringSink sink) {
3478-
renderSimple(c.instantiatedType, ast, r.template, sink,
3479-
parent: r, getters: _invisibleGetters['DartType']!);
3480-
},
3481-
),
34823470
'isPublic': Property(
34833471
getValue: (CT_ c) => c.isPublic,
34843472
renderVariable: (CT_ c, Property<CT_> self,
@@ -4083,18 +4071,6 @@ class _Renderer_ElementType extends RendererBase<ElementType> {
40834071
parent: r);
40844072
},
40854073
),
4086-
'instantiatedType': Property(
4087-
getValue: (CT_ c) => c.instantiatedType,
4088-
renderVariable: (CT_ c, Property<CT_> self,
4089-
List<String> remainingNames) =>
4090-
self.renderSimpleVariable(c, remainingNames, 'DartType'),
4091-
isNullValue: (CT_ c) => false,
4092-
renderValue: (CT_ c, RendererBase<CT_> r,
4093-
List<MustachioNode> ast, StringSink sink) {
4094-
renderSimple(c.instantiatedType, ast, r.template, sink,
4095-
parent: r, getters: _invisibleGetters['DartType']!);
4096-
},
4097-
),
40984074
'isTypedef': Property(
40994075
getValue: (CT_ c) => c.isTypedef,
41004076
renderVariable: (CT_ c, Property<CT_> self,
@@ -4611,8 +4587,8 @@ class _Renderer_Extension extends RendererBase<Extension> {
46114587
parent: r);
46124588
},
46134589
),
4614-
'extendedType': Property(
4615-
getValue: (CT_ c) => c.extendedType,
4590+
'extendedElement': Property(
4591+
getValue: (CT_ c) => c.extendedElement,
46164592
renderVariable:
46174593
(CT_ c, Property<CT_> self, List<String> remainingNames) {
46184594
if (remainingNames.isEmpty) {
@@ -4629,7 +4605,8 @@ class _Renderer_Extension extends RendererBase<Extension> {
46294605
isNullValue: (CT_ c) => false,
46304606
renderValue: (CT_ c, RendererBase<CT_> r,
46314607
List<MustachioNode> ast, StringSink sink) {
4632-
_render_ElementType(c.extendedType, ast, r.template, sink,
4608+
_render_ElementType(
4609+
c.extendedElement, ast, r.template, sink,
46334610
parent: r);
46344611
},
46354612
),

lib/src/model/extension.dart

+37-24
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analyzer/dart/element/element.dart';
6+
import 'package:analyzer/dart/element/nullability_suffix.dart';
67
import 'package:analyzer/dart/element/type.dart';
78
import 'package:dartdoc/src/element_type.dart';
89
import 'package:dartdoc/src/model/comment_referable.dart';
910
import 'package:dartdoc/src/model/model.dart';
10-
import 'package:dartdoc/src/type_utils.dart';
1111
import 'package:meta/meta.dart';
1212

1313
/// Static extension on a given type, containing methods (including getters,
@@ -16,37 +16,50 @@ class Extension extends Container {
1616
@override
1717
final ExtensionElement element;
1818

19-
late final ElementType extendedType =
19+
late final ElementType extendedElement =
2020
getTypeFor(element.extendedType, library);
2121

2222
Extension(this.element, super.library, super.packageGraph);
2323

24-
/// Detect if this extension applies to every object.
25-
bool get alwaysApplies =>
26-
extendedType.instantiatedType is DynamicType ||
27-
extendedType.instantiatedType is VoidType ||
28-
extendedType.instantiatedType.isDartCoreObject;
29-
30-
bool couldApplyTo<T extends InheritingContainer>(T c) =>
31-
_couldApplyTo(c.modelType);
24+
/// Whether this extension applies to every static type.
25+
bool get alwaysApplies {
26+
var extendedType = extendedElement.type;
27+
if (extendedType is TypeParameterType) extendedType = extendedType.bound;
28+
return extendedType is DynamicType ||
29+
extendedType is VoidType ||
30+
extendedType.isDartCoreObject;
31+
}
3232

33-
/// Whether this extension could apply to [type].
34-
bool _couldApplyTo(DefinedElementType type) {
35-
if (extendedType.instantiatedType is DynamicType ||
36-
extendedType.instantiatedType is VoidType) {
37-
return true;
33+
/// Whether this extension could apply to [container].
34+
///
35+
/// This makes some assumptions in its calculations. For example, all
36+
/// [InheritingContainer]s represent [InterfaceElement]s, so no care is taken
37+
/// to consider function types or record types.
38+
bool couldApplyTo(InheritingContainer container) {
39+
var extendedType = extendedElement.type;
40+
if (extendedType is TypeParameterType) {
41+
extendedType = extendedType.bound;
3842
}
39-
var typeInstantiated = type.instantiatedType;
40-
var extendedInstantiated = extendedType.instantiatedType;
41-
if (typeInstantiated == extendedInstantiated) {
43+
if (extendedType is DynamicType || extendedType is VoidType) {
4244
return true;
4345
}
44-
if (typeInstantiated.documentableElement ==
45-
extendedInstantiated.documentableElement &&
46-
extendedType.isSubtypeOf(type)) {
47-
return true;
46+
var otherType = container.modelType.type;
47+
if (otherType is InterfaceType) {
48+
otherType = library.element.typeSystem.instantiateInterfaceToBounds(
49+
element: otherType.element,
50+
nullabilitySuffix: NullabilitySuffix.none,
51+
);
52+
53+
for (var superType in [otherType, ...otherType.allSupertypes]) {
54+
var isSameBaseType = superType.element == extendedType.element;
55+
if (isSameBaseType &&
56+
library.element.typeSystem.isSubtypeOf(extendedType, superType)) {
57+
return true;
58+
}
59+
}
4860
}
49-
return extendedType.isBoundSupertypeTo(type);
61+
62+
return false;
5063
}
5164

5265
@override
@@ -100,7 +113,7 @@ class Extension extends Container {
100113
@override
101114
Map<String, CommentReferable> get referenceChildren {
102115
return _referenceChildren ??= {
103-
...extendedType.referenceChildren,
116+
...extendedElement.referenceChildren,
104117
// Override extendedType entries with local items.
105118
...super.referenceChildren,
106119
};

lib/src/model/inheriting_container.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ abstract class InheritingContainer extends Container {
271271
/// defined by [element] can exist where this extension applies, not including
272272
/// any extension that applies to every type.
273273
late final List<Extension> potentiallyApplicableExtensionsSorted =
274-
packageGraph.extensions.whereDocumented
274+
packageGraph.extensions
275275
.where((e) => !e.alwaysApplies)
276276
.where((e) => e.couldApplyTo(this))
277277
.toList(growable: false)

lib/src/model/package_graph.dart

+4-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ class PackageGraph with CommentReferable, Nameable {
160160
_addToImplementers(library.classesAndExceptions);
161161
_addToImplementers(library.mixins);
162162
_addToImplementers(library.extensionTypes);
163-
_extensions.addAll(library.extensions);
163+
if (library.isCanonical) {
164+
_extensions.addAll(library.extensions.whereDocumented);
165+
}
164166
}
165167
if (package.isLocal && !package.hasPublicLibraries) {
166168
package.warn(PackageWarning.noDocumentableLibrariesInPackage);
@@ -383,7 +385,7 @@ class PackageGraph with CommentReferable, Nameable {
383385
clazz.definingContainer.hashCode);
384386

385387
/// A list of extensions that exist in the package graph.
386-
final List<Extension> _extensions = [];
388+
final Set<Extension> _extensions = {};
387389

388390
/// Name of the default package.
389391
String get defaultPackageName => packageMeta.name;

lib/templates/_extension.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<dt id="{{ htmlId }}">
22
<span class="name {{ #isDeprecated }}deprecated{{ /isDeprecated }}">{{{ linkedName }}}</span>
3-
on {{{ extendedType.linkedName }}}
3+
on {{{ extendedElement.linkedName }}}
44
{{ >categorization }}
55
</dt>
66
<dd>

lib/templates/extension.html

+1-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
<dt>on</dt>
1616
<dd>
1717
<ul class="comma-separated clazz-relationships">
18-
{{ #extendedType }}
19-
<li>{{{ linkedName }}}</li>
20-
{{ /extendedType }}
18+
<li>{{{ extendedElement.linkedName }}}</li>
2119
</ul>
2220
</dd>
2321
</dl>

lib/templates/method.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<div id="dartdoc-sidebar-left" class="sidebar sidebar-offcanvas-left">
2525
{{ >search_sidebar }}
2626
{{ #isParentExtension }}
27-
<h5>{{ parent.name }} {{ parent.kind }} on {{{ parentAsExtension.extendedType.linkedName }}}</h5>
27+
<h5>{{ parent.name }} {{ parent.kind }} on {{{ parentAsExtension.extendedElement.linkedName }}}</h5>
2828
{{ /isParentExtension }}
2929
{{ ^isParentExtension }}
3030
<h5>{{ parent.name }} {{ parent.kind }}</h5>

lib/templates/property.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<div id="dartdoc-sidebar-left" class="sidebar sidebar-offcanvas-left">
3737
{{ >search_sidebar }}
3838
{{ #isParentExtension }}
39-
<h5>{{ parent.name }} {{ parent.kind }} on {{{ parentAsExtension.extendedType.linkedName }}}</h5>
39+
<h5>{{ parent.name }} {{ parent.kind }} on {{{ parentAsExtension.extendedElement.linkedName }}}</h5>
4040
{{ /isParentExtension }}
4141
{{ ^isParentExtension }}
4242
<h5>{{ parent.name }} {{ parent.kind }}</h5>

0 commit comments

Comments
 (0)