Skip to content

Commit 348cbd7

Browse files
authored
Basic implementation of typedef member lookups (#2768)
* flatten * Fix up typedef implementation to handle generic non-class correctly * oops, forgot a bit
1 parent c851212 commit 348cbd7

File tree

5 files changed

+83
-13
lines changed

5 files changed

+83
-13
lines changed

lib/src/model/model_element.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,10 @@ abstract class ModelElement extends Canonicalization
315315
if (e.aliasedType is FunctionType) {
316316
return FunctionTypedef(e, library, packageGraph);
317317
}
318-
return Typedef(e, library, packageGraph);
318+
if (e.aliasedType.element is ClassElement) {
319+
return ClassTypedef(e, library, packageGraph);
320+
}
321+
return GeneralizedTypedef(e, library, packageGraph);
319322
}
320323
if (e is ConstructorElement) {
321324
return Constructor(e, library, packageGraph);

lib/src/model/typedef.dart

+51-10
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'package:dartdoc/src/model/comment_referable.dart';
99
import 'package:dartdoc/src/model/model.dart';
1010
import 'package:dartdoc/src/render/typedef_renderer.dart';
1111

12-
class Typedef extends ModelElement
12+
abstract class Typedef extends ModelElement
1313
with TypeParameters, Categorization
1414
implements EnclosedElement {
1515
Typedef(TypeAliasElement element, Library library, PackageGraph packageGraph)
@@ -40,6 +40,8 @@ class Typedef extends ModelElement
4040
@override
4141
String get filePath => '${library.dirName}/$fileName';
4242

43+
/// Helper for mustache templates, which can't do casting themselves
44+
/// without this.
4345
FunctionTypedef get asCallable => this as FunctionTypedef;
4446

4547
@override
@@ -65,34 +67,73 @@ class Typedef extends ModelElement
6567

6668
TypedefRenderer get _renderer => packageGraph.rendererFactory.typedefRenderer;
6769

68-
Map<String, CommentReferable> _referenceChildren;
70+
@override
71+
Iterable<CommentReferable> get referenceParents => [definingLibrary];
6972

73+
Map<String, CommentReferable> _referenceChildren;
7074
@override
7175
Map<String, CommentReferable> get referenceChildren {
7276
if (_referenceChildren == null) {
7377
_referenceChildren = {};
74-
75-
// Only consider parameters if this is a function typedef.
76-
if (isCallable) {
77-
_referenceChildren
78-
.addEntriesIfAbsent(parameters.explicitOnCollisionWith(this));
79-
}
8078
_referenceChildren
8179
.addEntriesIfAbsent(typeParameters.explicitOnCollisionWith(this));
8280
}
8381
return _referenceChildren;
8482
}
83+
}
84+
85+
/// A typedef referring to a non-function typedef that is nevertheless not
86+
/// referring to a defined class. An example is a typedef alias for `void` or
87+
/// for `Function` itself.
88+
class GeneralizedTypedef extends Typedef {
89+
GeneralizedTypedef(
90+
TypeAliasElement element, Library library, PackageGraph packageGraph)
91+
: super(element, library, packageGraph) {
92+
assert(!isCallable);
93+
}
94+
}
95+
96+
/// A typedef referring to a non-function, defined type.
97+
class ClassTypedef extends Typedef {
98+
ClassTypedef(
99+
TypeAliasElement element, Library library, PackageGraph packageGraph)
100+
: super(element, library, packageGraph) {
101+
assert(!isCallable);
102+
assert(modelType.modelElement is Class);
103+
}
85104

86105
@override
87-
Iterable<CommentReferable> get referenceParents => [definingLibrary];
106+
DefinedElementType get modelType => super.modelType;
107+
108+
@override
109+
Map<String, CommentReferable> get referenceChildren {
110+
if (_referenceChildren == null) {
111+
_referenceChildren = super.referenceChildren;
112+
_referenceChildren
113+
.addEntriesIfAbsent(modelType.modelElement.referenceChildren.entries);
114+
}
115+
return _referenceChildren;
116+
}
88117
}
89118

90119
/// A typedef referring to a function type.
91120
class FunctionTypedef extends Typedef {
92121
FunctionTypedef(
93122
TypeAliasElement element, Library library, PackageGraph packageGraph)
94-
: super(element, library, packageGraph);
123+
: super(element, library, packageGraph) {
124+
assert(isCallable);
125+
}
95126

96127
@override
97128
Callable get modelType => super.modelType;
129+
130+
@override
131+
Map<String, CommentReferable> get referenceChildren {
132+
if (_referenceChildren == null) {
133+
_referenceChildren = super.referenceChildren;
134+
_referenceChildren
135+
.addEntriesIfAbsent(parameters.explicitOnCollisionWith(this));
136+
}
137+
return _referenceChildren;
138+
}
98139
}

test/end2end/model_special_cases_test.dart

+17
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,23 @@ void main() {
186186
expect(referenceLookup(constructorTearoffs, 'F.new'),
187187
equals(MatchingLinkResult(Fnew)));
188188
});
189+
190+
test('.new works on typedefs', () {
191+
expect(referenceLookup(constructorTearoffs, 'At.new'),
192+
equals(MatchingLinkResult(Anew)));
193+
expect(referenceLookup(constructorTearoffs, 'Bt.new'),
194+
equals(MatchingLinkResult(Bnew)));
195+
expect(referenceLookup(constructorTearoffs, 'Ct.new'),
196+
equals(MatchingLinkResult(Cnew)));
197+
expect(referenceLookup(constructorTearoffs, 'Dt.new'),
198+
equals(MatchingLinkResult(Dnew)));
199+
expect(referenceLookup(constructorTearoffs, 'Et.new'),
200+
equals(MatchingLinkResult(Enew)));
201+
expect(referenceLookup(constructorTearoffs, 'Fstring.new'),
202+
equals(MatchingLinkResult(Fnew)));
203+
expect(referenceLookup(constructorTearoffs, 'Ft.new'),
204+
equals(MatchingLinkResult(Fnew)));
205+
});
189206
}, skip: !_constructorTearoffsAllowed.allows(utils.platformVersion));
190207
});
191208

test/end2end/model_test.dart

+10-1
Original file line numberDiff line numberDiff line change
@@ -2236,7 +2236,8 @@ void main() {
22362236
group('Linking for generalized typedef cases works', () {
22372237
Library generalizedTypedefs;
22382238
Typedef T0, T2, T5, T8;
2239-
Class C2;
2239+
Class C1, C2;
2240+
Field C1a;
22402241

22412242
setUpAll(() {
22422243
generalizedTypedefs = packageGraph.libraries
@@ -2245,7 +2246,9 @@ void main() {
22452246
T2 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T2');
22462247
T5 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T5');
22472248
T8 = generalizedTypedefs.typedefs.firstWhere((a) => a.name == 'T8');
2249+
C1 = generalizedTypedefs.classes.firstWhere((c) => c.name == 'C1');
22482250
C2 = generalizedTypedefs.classes.firstWhere((c) => c.name == 'C2');
2251+
C1a = C1.allFields.firstWhere((f) => f.name == 'a');
22492252
});
22502253

22512254
test('Verify basic ability to link anything', () {
@@ -2266,6 +2269,12 @@ void main() {
22662269
var T5name = T5.parameters.firstWhere((t) => t.name == 'name');
22672270
expect(referenceLookup(T5, 'name'), equals(MatchingLinkResult(T5name)));
22682271
});
2272+
2273+
test('Verify ability to link to class members of aliased classes', () {
2274+
expect(referenceLookup(generalizedTypedefs, 'T8.a'),
2275+
equals(MatchingLinkResult(C1a)));
2276+
expect(referenceLookup(T8, 'a'), equals(MatchingLinkResult(C1a)));
2277+
});
22692278
});
22702279

22712280
group('Linking for complex inheritance and reexport cases', () {

testing/test_package_experiments/lib/constructor_tearoffs.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ typedef NotAClass = Function;
5858

5959
/// Mixins don't have constructors either, so disallow `M.new`.
6060
mixin M<T> on C {
61-
}
61+
}

0 commit comments

Comments
 (0)