Skip to content

Commit ce5bd27

Browse files
authored
Fix generic typedef pointing to typedef (#3193)
1 parent 64805fc commit ce5bd27

File tree

6 files changed

+148
-27
lines changed

6 files changed

+148
-27
lines changed

lib/src/model/model_element.dart

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ abstract class ModelElement extends Canonicalization
181181
return cachedModelElement;
182182
}
183183

184-
ModelElement? newModelElement;
184+
ModelElement newModelElement;
185185
if (e is FieldElement) {
186186
if (enclosingContainer == null) {
187187
if (e.isEnumConstant) {
@@ -639,7 +639,7 @@ abstract class ModelElement extends Canonicalization
639639
return self.enclosingElement == self.canonicalEnclosingContainer;
640640
}
641641

642-
/// Returns the docs, stripped of their leading comments syntax.
642+
/// The documentaion, stripped of its comment syntax, like `///` characters.
643643
@override
644644
String get documentation {
645645
return injectMacros(
@@ -830,38 +830,45 @@ abstract class ModelElement extends Canonicalization
830830
@override
831831
p.Context get pathContext => packageGraph.resourceProvider.pathContext;
832832

833+
// TODO(srawlins): This really smells like it should just be implemented in
834+
// the subclasses.
833835
late final List<Parameter> parameters = () {
836+
final element = this.element;
834837
if (!isCallable) {
835838
throw StateError(
836839
'$element (${element.runtimeType}) cannot have parameters');
837840
}
838841

842+
final List<ParameterElement> params;
839843
if (element is TypeAliasElement) {
840-
return ModelElement._fromElement(
841-
(element as TypeAliasElement).aliasedElement!, packageGraph)
842-
.parameters;
843-
}
844-
List<ParameterElement>? params;
845-
if (element is ExecutableElement) {
844+
final aliasedType = element.aliasedType;
845+
if (aliasedType is FunctionType) {
846+
params = aliasedType.parameters;
847+
} else {
848+
return const <Parameter>[];
849+
}
850+
} else if (element is ExecutableElement) {
846851
if (_originalMember != null) {
847852
assert(_originalMember is ExecutableMember);
848853
params = (_originalMember as ExecutableMember).parameters;
849854
} else {
850-
params = (element as ExecutableElement).parameters;
855+
params = element.parameters;
851856
}
852-
}
853-
if (params == null && element is FunctionTypedElement) {
857+
} else if (element is FunctionTypedElement) {
854858
if (_originalMember != null) {
855859
params = (_originalMember as FunctionTypedElement).parameters;
856860
} else {
857-
params = (element as FunctionTypedElement).parameters;
861+
params = element.parameters;
858862
}
863+
} else {
864+
return const <Parameter>[];
859865
}
860866

861-
return <Parameter>[
862-
...?params?.map(
863-
(p) => ModelElement._from(p, library, packageGraph) as Parameter)
864-
];
867+
return List.of(
868+
params.map(
869+
(p) => ModelElement._from(p, library, packageGraph) as Parameter),
870+
growable: false,
871+
);
865872
}();
866873

867874
@override

lib/src/model/package.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class Package extends LibraryContainer
117117
@override
118118
late final String documentationAsHtml = Documentation.forElement(this).asHtml;
119119

120+
/// The documentation from the README contents.
120121
@override
121122
late final String? documentation = () {
122123
final docFile = packageMeta.getReadmeContents();

test/end2end/model_test.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4521,15 +4521,19 @@ String? topLevelFunction(int param1, bool param2, Cool coolBeans,
45214521
test('anonymous nested functions inside typedefs are handled correctly',
45224522
() {
45234523
expect(
4524-
aComplexTypedef.modelType.returnType.linkedName,
4525-
equals(
4526-
'void Function<span class="signature">(<span class="parameter" id="param-"><span class="type-annotation">A1</span>, </span><span class="parameter" id="param-"><span class="type-annotation">A2</span>, </span><span class="parameter" id="param-"><span class="type-annotation">A3</span></span>)</span>'));
4524+
aComplexTypedef.modelType.returnType.linkedName,
4525+
'void Function<span class="signature">'
4526+
'(<span class="parameter" id="param-"><span class="type-annotation">A1</span>, '
4527+
'</span><span class="parameter" id="param-"><span class="type-annotation">A2</span>, </span>'
4528+
'<span class="parameter" id="param-"><span class="type-annotation">A3</span></span>)</span>',
4529+
);
45274530
expect(
4528-
aComplexTypedef.linkedParamsLines,
4529-
equals(
4530-
'<ol class="parameter-list"><li><span class="parameter" id="aComplexTypedef-param-"><span class="type-annotation">A3</span>, </span></li>\n'
4531-
'<li><span class="parameter" id="aComplexTypedef-param-"><span class="type-annotation">String</span></span></li>\n'
4532-
'</ol>'));
4531+
aComplexTypedef.linkedParamsLines,
4532+
'<ol class="parameter-list">'
4533+
'<li><span class="parameter" id="param-"><span class="type-annotation">A3</span>, </span></li>\n'
4534+
'<li><span class="parameter" id="param-"><span class="type-annotation">String</span></span></li>\n'
4535+
'</ol>',
4536+
);
45334537
});
45344538

45354539
test('has a fully qualified name', () {

test/src/test_descriptor_utils.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const _defaultPubspec = '''
1414
name: test_package
1515
version: 0.0.1
1616
environment:
17-
sdk: '>=2.12.0 <3.0.0'
17+
sdk: '>=2.15.0 <3.0.0'
1818
''';
1919

2020
/// Creates a pub package in a directory named [name].

test/src/utils.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ void runPubGet(String dirPath) {
8989
}
9090
}
9191

92+
/// Creates a package at [dirPath] and returns the [PackageGraph] built with
93+
/// [PubPackageBuilder.buildPackageGraph].
9294
Future<PackageGraph> bootBasicPackage(
9395
String dirPath,
9496
PackageMetaProvider packageMetaProvider,
@@ -97,11 +99,11 @@ Future<PackageGraph> bootBasicPackage(
9799
List<String> additionalArguments = const [],
98100
bool skipUnreachableSdkLibraries = true,
99101
}) async {
100-
var resourceProvider = packageMetaProvider.resourceProvider;
102+
final resourceProvider = packageMetaProvider.resourceProvider;
101103
if (resourceProvider == PhysicalResourceProvider.INSTANCE) {
102104
runPubGet(dirPath);
103105
}
104-
var dir = resourceProvider.getFolder(resourceProvider.pathContext
106+
final dir = resourceProvider.getFolder(resourceProvider.pathContext
105107
.absolute(resourceProvider.pathContext.normalize(dirPath)));
106108
return PubPackageBuilder(
107109
await contextFromArgv([

test/typedef_test.dart

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/element/type.dart';
6+
import 'package:analyzer/file_system/memory_file_system.dart';
7+
import 'package:dartdoc/src/model/model.dart';
8+
import 'package:test/test.dart';
9+
10+
import 'src/test_descriptor_utils.dart' as d;
11+
import 'src/utils.dart';
12+
13+
void main() {
14+
group('typedefs', () {
15+
late Library library;
16+
17+
// It is expensive (~10s) to compute a package graph, even skipping
18+
// unreachable Dart SDK libraries, so we set up this package once.
19+
setUpAll(() async {
20+
const libraryName = 'typedefs';
21+
final packageMetaProvider = testPackageMetaProvider;
22+
23+
final packagePath = await d.createPackage(
24+
libraryName,
25+
libFiles: [
26+
d.file('lib.dart', '''
27+
library $libraryName;
28+
29+
/// Line _one_.
30+
///
31+
/// Line _two_.
32+
typedef Cb1 = void Function();
33+
34+
typedef Cb2<T> = T Function(T);
35+
36+
typedef Cb3<T> = Cb2<List<T>>;
37+
'''),
38+
],
39+
resourceProvider:
40+
packageMetaProvider.resourceProvider as MemoryResourceProvider,
41+
);
42+
final packageConfigProvider =
43+
getTestPackageConfigProvider(packageMetaProvider.defaultSdkDir.path);
44+
packageConfigProvider.addPackageToConfigFor(
45+
packagePath, libraryName, Uri.file('$packagePath/'));
46+
47+
final packageGraph = await bootBasicPackage(
48+
packagePath,
49+
packageMetaProvider,
50+
packageConfigProvider,
51+
);
52+
library = packageGraph.libraries.named(libraryName);
53+
});
54+
55+
test('basic typedef', () async {
56+
final cb1Typedef = library.typedefs.named('Cb1');
57+
58+
expect(cb1Typedef.nameWithGenerics, 'Cb1');
59+
expect(cb1Typedef.genericParameters, '');
60+
expect(cb1Typedef.aliasedType is FunctionType, isTrue);
61+
expect(cb1Typedef.documentationComment, '''
62+
/// Line _one_.
63+
///
64+
/// Line _two_.''');
65+
expect(cb1Typedef.documentation, '''
66+
Line _one_.
67+
68+
Line _two_.''');
69+
expect(cb1Typedef.oneLineDoc, 'Line <em>one</em>.');
70+
expect(cb1Typedef.documentationAsHtml, '''
71+
<p>Line <em>one</em>.</p>
72+
<p>Line <em>two</em>.</p>''');
73+
});
74+
75+
test('generic typedef', () async {
76+
final cb2Typedef = library.typedefs.named('Cb2');
77+
78+
expect(
79+
cb2Typedef.nameWithGenerics,
80+
'Cb2&lt;<wbr><span class="type-parameter">T</span>&gt;',
81+
);
82+
expect(
83+
cb2Typedef.genericParameters,
84+
'&lt;<wbr><span class="type-parameter">T</span>&gt;',
85+
);
86+
expect(cb2Typedef.aliasedType is FunctionType, isTrue);
87+
});
88+
89+
test('generic typedef referring to a generic typedef', () async {
90+
final cb3Typedef = library.typedefs.named('Cb3');
91+
92+
expect(
93+
cb3Typedef.nameWithGenerics,
94+
'Cb3&lt;<wbr><span class="type-parameter">T</span>&gt;',
95+
);
96+
expect(
97+
cb3Typedef.genericParameters,
98+
'&lt;<wbr><span class="type-parameter">T</span>&gt;',
99+
);
100+
expect(cb3Typedef.aliasedType is FunctionType, isTrue);
101+
102+
expect(cb3Typedef.parameters, hasLength(1));
103+
104+
// TODO(srawlins): Dramatically improve typedef testing.
105+
});
106+
});
107+
}

0 commit comments

Comments
 (0)