Skip to content

Commit daaaf3d

Browse files
authored
Allow for anonymous typed functions (#1506)
* Basic functionality for typed functions without typedefs * Refactor and add more tests * dartfmt * Move constructor logic to a more logical place * Make AppVeyor run pub get for the test package as well to regenerate .packages * Doc update * appveyor experiment * appveyor experiment 2 * appveyor experiment #3 * update pubspec.locks * Fix private detection to include dart:_ prefix * travis experiment * travis experiment 2 * travis experiment 3 * Reset to 05ddca0 * Fix private detection to include dart:_ prefix * Review comments * typo
1 parent 43ada27 commit daaaf3d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1581
-50
lines changed

appveyor.yml

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ install:
99
- set PATH=%PATH%;C:\tools\dart-sdk\bin
1010
- set PATH=%PATH%;%APPDATA%\Pub\Cache\bin
1111
- pub get
12+
- cmd: cd testing
13+
- cmd: cd test_package
14+
- cmd: pub get
15+
- cmd: cd ..
16+
- cmd: cd ..
1217

1318
build: off
1419

lib/src/element_type.dart

+24-28
Original file line numberDiff line numberDiff line change
@@ -21,42 +21,38 @@ class ElementType {
2121

2222
bool get isFunctionType => (_type is FunctionType);
2323

24-
bool get isParameterizedType {
25-
if (_type is FunctionType) {
26-
return typeArguments.isNotEmpty;
27-
} else if (_type is ParameterizedType) {
28-
return (_type as ParameterizedType).typeArguments.isNotEmpty;
29-
}
30-
return false;
31-
}
24+
bool get isParameterizedType => (_type is ParameterizedType);
3225

3326
bool get isParameterType => (_type is TypeParameterType);
3427

3528
String get linkedName {
36-
if (_linkedName != null) return _linkedName;
37-
38-
StringBuffer buf = new StringBuffer();
39-
40-
if (isParameterType) {
41-
buf.write(name);
42-
} else {
43-
buf.write(element.linkedName);
44-
}
29+
if (_linkedName == null) {
30+
StringBuffer buf = new StringBuffer();
4531

46-
// not TypeParameterType or Void or Union type
47-
if (isParameterizedType) {
48-
if (typeArguments.every((t) => t.linkedName == 'dynamic')) {
49-
_linkedName = buf.toString();
50-
return _linkedName;
32+
if (isParameterType) {
33+
buf.write(name);
34+
} else {
35+
buf.write(element.linkedName);
5136
}
52-
if (typeArguments.isNotEmpty) {
53-
buf.write('<');
54-
buf.writeAll(typeArguments.map((t) => t.linkedName), ', ');
55-
buf.write('>');
37+
38+
// not TypeParameterType or Void or Union type
39+
if (isParameterizedType) {
40+
if (!typeArguments.every((t) => t.linkedName == 'dynamic') &&
41+
typeArguments.isNotEmpty) {
42+
buf.write('<');
43+
buf.writeAll(typeArguments.map((t) => t.linkedName), ', ');
44+
buf.write('>');
45+
}
46+
// Hide parameters if there's a an explicit typedef behind this
47+
// element, but if there is no typedef, be explicit.
48+
if (element is ModelFunctionAnonymous) {
49+
buf.write('(');
50+
buf.write(element.linkedParams());
51+
buf.write(')');
52+
}
5653
}
54+
_linkedName = buf.toString();
5755
}
58-
_linkedName = buf.toString();
59-
6056
return _linkedName;
6157
}
6258

lib/src/model.dart

+54-13
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ class Class extends ModelElement implements EnclosedElement {
769769

770770
@override
771771
String get nameWithGenerics {
772-
if (!modelType.isParameterizedType) return name;
772+
if (!modelType.isParameterizedType || _typeParameters.isEmpty) return name;
773773
return '$name<${_typeParameters.map((t) => t.name).join(', ')}>';
774774
}
775775

@@ -2216,6 +2216,14 @@ abstract class ModelElement extends Nameable
22162216
}
22172217
if (e is FunctionElement) {
22182218
newModelElement = new ModelFunction(e, library);
2219+
} else if (e is GenericFunctionTypeElement) {
2220+
if (e is FunctionTypeAliasElement) {
2221+
assert(e.name != '');
2222+
newModelElement = new ModelFunctionTypedef(e, library);
2223+
} else {
2224+
assert(e.name == '');
2225+
newModelElement = new ModelFunctionAnonymous(e, library);
2226+
}
22192227
}
22202228
if (e is FunctionTypeAliasElement) {
22212229
newModelElement = new Typedef(e, library);
@@ -2285,10 +2293,7 @@ abstract class ModelElement extends Nameable
22852293
}
22862294
}
22872295
}
2288-
// TODO(jcollins-g): Consider subclass for ModelFunctionTyped.
2289-
if (e is GenericFunctionTypeElement) {
2290-
newModelElement = new ModelFunctionTyped(e, library);
2291-
}
2296+
22922297

22932298
if (newModelElement == null) throw "Unknown type ${e.runtimeType}";
22942299
if (enclosingClass != null) assert(newModelElement is Inheritable);
@@ -3203,6 +3208,7 @@ abstract class ModelElement extends Nameable
32033208
}
32043209
}
32053210

3211+
/// A [ModelElement] for a [FunctionElement] that isn't part of a type definition.
32063212
class ModelFunction extends ModelFunctionTyped {
32073213
ModelFunction(FunctionElement element, Library library)
32083214
: super(element, library);
@@ -3212,10 +3218,52 @@ class ModelFunction extends ModelFunctionTyped {
32123218
return _func.isStatic;
32133219
}
32143220

3221+
@override
3222+
String get name {
3223+
if (element.enclosingElement is ParameterElement && super.name.isEmpty)
3224+
return element.enclosingElement.name;
3225+
return super.name;
3226+
}
3227+
32153228
@override
32163229
FunctionElement get _func => (element as FunctionElement);
32173230
}
32183231

3232+
/// A [ModelElement] for a [GenericModelFunctionElement] that is an
3233+
/// explicit typedef.
3234+
///
3235+
/// Distinct from ModelFunctionTypedef in that it doesn't
3236+
/// have a name, but we document it as "Function" to match how these are
3237+
/// written in declarations.
3238+
class ModelFunctionAnonymous extends ModelFunctionTyped {
3239+
ModelFunctionAnonymous(FunctionTypedElement element, Library library)
3240+
: super(element, library) {}
3241+
3242+
@override
3243+
String get name => 'Function';
3244+
3245+
@override
3246+
bool get isPublic => false;
3247+
}
3248+
3249+
/// A [ModelElement] for a [GenericModelFunctionElement] that is part of an
3250+
/// explicit typedef.
3251+
class ModelFunctionTypedef extends ModelFunctionTyped {
3252+
ModelFunctionTypedef(FunctionTypedElement element, Library library)
3253+
: super(element, library) {}
3254+
3255+
@override
3256+
String get name {
3257+
Element e = element;
3258+
while (e != null) {
3259+
if (e is FunctionTypeAliasElement) return e.name;
3260+
e = e.enclosingElement;
3261+
}
3262+
assert(false);
3263+
return super.name;
3264+
}
3265+
}
3266+
32193267
class ModelFunctionTyped extends ModelElement
32203268
with SourceCodeMixin
32213269
implements EnclosedElement {
@@ -3238,13 +3286,6 @@ class ModelFunctionTyped extends ModelElement
32383286

32393287
String get fileName => "$name.html";
32403288

3241-
@override
3242-
String get name {
3243-
if (element.enclosingElement is ParameterElement && super.name.isEmpty)
3244-
return element.enclosingElement.name;
3245-
return super.name;
3246-
}
3247-
32483289
@override
32493290
String get href {
32503291
if (canonicalLibrary == null) return null;
@@ -4590,7 +4631,7 @@ class Typedef extends ModelElement
45904631

45914632
@override
45924633
String get nameWithGenerics {
4593-
if (!modelType.isParameterizedType) return name;
4634+
if (!modelType.isParameterizedType || _typeParameters.isEmpty) return name;
45944635
return '$name<${_typeParameters.map((t) => t.name).join(', ')}>';
45954636
}
45964637

lib/src/model_utils.dart

+6-4
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@ bool isInExportedLibraries(
5454

5555
final RegExp slashes = new RegExp('[\/]');
5656
bool hasPrivateName(Element e) {
57-
if (e.name.startsWith('_') ||
58-
(e is LibraryElement &&
59-
(e.identifier == 'dart:_internal' ||
60-
e.identifier == 'dart:nativewrappers'))) {
57+
if (e.name.startsWith('_')) {
6158
return true;
6259
}
60+
if (e is LibraryElement &&
61+
(e.identifier.startsWith('dart:_') ||
62+
['dart:nativewrappers'].contains(e.identifier))) {
63+
return true;
64+
}
6365
if (e is LibraryElement) {
6466
List<String> locationParts = e.location.components[0].split(slashes);
6567
// TODO(jcollins-g): Implement real cross package detection

pubspec.lock

+14-2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ packages:
121121
url: "https://pub.dartlang.org"
122122
source: hosted
123123
version: "1.0.0"
124+
js:
125+
description:
126+
name: js
127+
url: "https://pub.dartlang.org"
128+
source: hosted
129+
version: "0.6.1"
124130
kernel:
125131
description:
126132
name: kernel
@@ -169,6 +175,12 @@ packages:
169175
url: "https://pub.dartlang.org"
170176
source: hosted
171177
version: "1.1.0"
178+
node_preamble:
179+
description:
180+
name: node_preamble
181+
url: "https://pub.dartlang.org"
182+
source: hosted
183+
version: "1.4.0"
172184
package_config:
173185
description:
174186
name: package_config
@@ -294,7 +306,7 @@ packages:
294306
name: test
295307
url: "https://pub.dartlang.org"
296308
source: hosted
297-
version: "0.12.20+13"
309+
version: "0.12.24+6"
298310
tuple:
299311
description:
300312
name: tuple
@@ -350,4 +362,4 @@ packages:
350362
source: hosted
351363
version: "2.1.12"
352364
sdks:
353-
dart: ">=1.23.0-dev.11.5 <2.0.0-dev.infinity"
365+
dart: ">=1.23.0 <=2.0.0-dev.2.0"

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ dev_dependencies:
2828
http: ^0.11.0
2929
meta: ^1.0.0
3030
pub_semver: ^1.0.0
31-
test: ^0.12.0
31+
test: '^0.12.20+24'
3232
executables:
3333
dartdoc: null

test/model_test.dart

+27-2
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ void main() {
686686
});
687687

688688
test('correctly finds all the classes', () {
689-
expect(classes, hasLength(21));
689+
expect(classes, hasLength(22));
690690
});
691691

692692
test('abstract', () {
@@ -1027,9 +1027,10 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
10271027
});
10281028

10291029
group('Method', () {
1030-
Class classB, klass, HasGenerics, Cat, CatString;
1030+
Class classB, klass, HasGenerics, Cat, CatString, TypedFunctionsWithoutTypedefs;
10311031
Method m1, isGreaterThan, m4, m5, m6, m7, convertToMap, abstractMethod;
10321032
Method inheritedClear, testGeneric, testGenericMethod;
1033+
Method getAFunctionReturningVoid;
10331034

10341035
setUp(() {
10351036
klass = exLibrary.classes.singleWhere((c) => c.name == 'Klass');
@@ -1061,6 +1062,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
10611062
.singleWhere((m) => m.name == 'testGenericMethod');
10621063
convertToMap = HasGenerics.instanceMethods
10631064
.singleWhere((m) => m.name == 'convertToMap');
1065+
TypedFunctionsWithoutTypedefs = exLibrary.classes.singleWhere((c) => c.name == 'TypedFunctionsWithoutTypedefs');
1066+
getAFunctionReturningVoid = TypedFunctionsWithoutTypedefs.instanceMethods.singleWhere((m) => m.name == 'getAFunctionReturningVoid');
10641067
});
10651068

10661069
tearDown(() {
@@ -1070,6 +1073,15 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
10701073
}
10711074
});
10721075

1076+
test('verify parameters to types are displayed', () {
1077+
var matcher = new RegExp('Function\\(<span class="parameter" id="getAFunctionReturningVoid-param-"><span class="type-annotation">T.</span></span> <span class="parameter" id="getAFunctionReturningVoid-param-"><span class="type-annotation">T.</span></span>\\)');
1078+
expect(matcher.hasMatch(getAFunctionReturningVoid.linkedReturnType), isTrue);
1079+
});
1080+
1081+
test('verify parameter types are correctly displayed', () {
1082+
expect(getAFunctionReturningVoid.linkedReturnType, equals('Function(<span class="parameter" id="getAFunctionReturningVoid-param-"><span class="type-annotation">T1</span></span> <span class="parameter" id="getAFunctionReturningVoid-param-"><span class="type-annotation">T2</span></span>)'));
1083+
}, skip: 'blocked on https://github.com/dart-lang/sdk/issues/30146');
1084+
10731085
test('has a fully qualified name', () {
10741086
expect(m1.fullyQualifiedName, 'ex.B.m1');
10751087
});
@@ -1757,13 +1769,26 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
17571769
group('Typedef', () {
17581770
Typedef t;
17591771
Typedef generic;
1772+
Typedef aComplexTypedef;
17601773

17611774
setUp(() {
17621775
t = exLibrary.typedefs.firstWhere((t) => t.name == 'processMessage');
17631776
generic =
17641777
fakeLibrary.typedefs.firstWhere((t) => t.name == 'NewGenericTypedef');
1778+
aComplexTypedef = exLibrary.typedefs.firstWhere((t) => t.name == 'aComplexTypedef');
17651779
});
17661780

1781+
test('anonymous nested functions inside typedefs are handled', () {
1782+
expect(aComplexTypedef, isNotNull);
1783+
expect(aComplexTypedef.linkedReturnType, startsWith('Function'));
1784+
expect(aComplexTypedef.nameWithGenerics, equals('aComplexTypedef&lt;A1, A2, A3&gt;'));
1785+
});
1786+
1787+
test('anonymous nested functions inside typedefs are handled correctly', () {
1788+
expect(aComplexTypedef.linkedReturnType, equals('Function(<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>)'));
1789+
expect(aComplexTypedef.linkedParamsLines, equals('<span class="parameter" id="aComplexTypedef-param-"><span class="type-annotation">A3</span></span> <span class="parameter" id="aComplexTypedef-param-"><span class="type-annotation">String</span></span>'));
1790+
}, skip: 'blocked on https://github.com/dart-lang/sdk/issues/30146');
1791+
17671792
test('has a fully qualified name', () {
17681793
expect(t.fullyQualifiedName, 'ex.processMessage');
17691794
expect(generic.fullyQualifiedName, 'fake.NewGenericTypedef');

testing/test_package/lib/example.dart

+13
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,16 @@ class _RetainedEnum {
424424
@override
425425
String toString() => name;
426426
}
427+
428+
/// Someone might do this some day.
429+
typedef aComplexTypedef<A1, A2, A3> = void Function(A1, A2, A3) Function(A3, String);
430+
431+
/// This class has a complicated type situation.
432+
abstract class TypedFunctionsWithoutTypedefs {
433+
/// Returns a function that returns a void with some generic types sprinkled in.
434+
void Function(T1, T2) getAFunctionReturningVoid<T1, T2>(
435+
void callback(T1 argument1, T2 argument2));
436+
437+
/// Returns a complex typedef that includes some anonymous typed functions.
438+
aComplexTypedef getAComplexTypedef<A4, A5, A6>();
439+
}

testing/test_package_docs/ex/Animal-class.html

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ <h5>library ex</h5>
5858
<li><a href="ex/PublicClassImplementsPrivateInterface-class.html">PublicClassImplementsPrivateInterface</a></li>
5959
<li><a href="ex/ShapeType-class.html">ShapeType</a></li>
6060
<li><a href="ex/SpecializedDuration-class.html">SpecializedDuration</a></li>
61+
<li><a href="ex/TypedFunctionsWithoutTypedefs-class.html">TypedFunctionsWithoutTypedefs</a></li>
6162
<li><a href="ex/WithGeneric-class.html">WithGeneric</a></li>
6263
<li><a href="ex/WithGenericSub-class.html">WithGenericSub</a></li>
6364

@@ -87,6 +88,7 @@ <h5>library ex</h5>
8788
<li><a href="ex/Animal-class.html">Animal</a></li>
8889

8990
<li class="section-title"><a href="ex/ex-library.html#typedefs">Typedefs</a></li>
91+
<li><a href="ex/aComplexTypedef.html">aComplexTypedef</a></li>
9092
<li><a href="ex/ParameterizedTypedef.html">ParameterizedTypedef</a></li>
9193
<li><a href="ex/processMessage.html">processMessage</a></li>
9294

testing/test_package_docs/ex/Apple-class.html

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ <h5>library ex</h5>
5858
<li><a href="ex/PublicClassImplementsPrivateInterface-class.html">PublicClassImplementsPrivateInterface</a></li>
5959
<li><a href="ex/ShapeType-class.html">ShapeType</a></li>
6060
<li><a href="ex/SpecializedDuration-class.html">SpecializedDuration</a></li>
61+
<li><a href="ex/TypedFunctionsWithoutTypedefs-class.html">TypedFunctionsWithoutTypedefs</a></li>
6162
<li><a href="ex/WithGeneric-class.html">WithGeneric</a></li>
6263
<li><a href="ex/WithGenericSub-class.html">WithGenericSub</a></li>
6364

@@ -87,6 +88,7 @@ <h5>library ex</h5>
8788
<li><a href="ex/Animal-class.html">Animal</a></li>
8889

8990
<li class="section-title"><a href="ex/ex-library.html#typedefs">Typedefs</a></li>
91+
<li><a href="ex/aComplexTypedef.html">aComplexTypedef</a></li>
9092
<li><a href="ex/ParameterizedTypedef.html">ParameterizedTypedef</a></li>
9193
<li><a href="ex/processMessage.html">processMessage</a></li>
9294

0 commit comments

Comments
 (0)