Skip to content

Commit 587a2e2

Browse files
authored
Initial implementation of constructor tearoff support (#2763)
* Initial implementation of constructor tearoff support * Move older experiments out now that the beta branch exists * initial review comments
1 parent 462fce2 commit 587a2e2

12 files changed

+317
-157
lines changed

lib/src/model/constructor.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,13 @@ class Constructor extends ModelElement
6767
@override
6868
bool get isConst => element.isConst;
6969

70-
bool get isUnnamedConstructor => name == enclosingElement.name;
70+
bool get isUnnamedConstructor =>
71+
name == enclosingElement.name || name == '${enclosingElement.name}.new';
7172

7273
@Deprecated(
74+
// TODO(jcollins-g): This, in retrospect, seems like a bad idea.
75+
// We should disambiguate the two concepts (default and unnamed) and
76+
// allow both if useful.
7377
'Renamed to `isUnnamedConstructor`; this getter with the old name will '
7478
'be removed as early as Dartdoc 1.0.0')
7579
bool get isDefaultConstructor => isUnnamedConstructor;
@@ -144,5 +148,5 @@ class Constructor extends ModelElement
144148

145149
@override
146150
String get referenceName =>
147-
element.name == '' ? enclosingElement.name : element.name;
151+
isUnnamedConstructor ? enclosingElement.name : element.name;
148152
}

test/end2end/dartdoc_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import 'package:test/test.dart';
2020
import '../src/utils.dart';
2121

2222
final _experimentPackageAllowed =
23-
VersionRange(min: Version.parse('2.14.0-0'), includeMin: true);
23+
VersionRange(min: Version.parse('2.15.0-0'), includeMin: true);
2424

2525
final _resourceProvider = pubPackageMetaProvider.resourceProvider;
2626
final _pathContext = _resourceProvider.pathContext;

test/end2end/model_special_cases_test.dart

Lines changed: 90 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ library dartdoc.model_special_cases_test;
1010

1111
import 'dart:io';
1212

13-
import 'package:analyzer/dart/element/type.dart';
1413
import 'package:async/async.dart';
14+
import 'package:dartdoc/src/matching_link_result.dart';
1515
import 'package:dartdoc/src/model/model.dart';
1616
import 'package:dartdoc/src/package_config_provider.dart';
1717
import 'package:dartdoc/src/package_meta.dart';
@@ -20,6 +20,7 @@ import 'package:pub_semver/pub_semver.dart';
2020
import 'package:test/test.dart';
2121

2222
import '../src/utils.dart' as utils;
23+
import '../src/utils.dart';
2324

2425
final _testPackageGraphExperimentsMemo = AsyncMemoizer<PackageGraph>();
2526
Future<PackageGraph> get _testPackageGraphExperiments =>
@@ -71,132 +72,106 @@ void main() {
7172
// ExperimentalFeature.experimentalReleaseVersion as these are set to null
7273
// even when partial analyzer implementations are available, and are often
7374
// set too high after release.
74-
final _genericMetadataAllowed =
75-
VersionRange(min: Version.parse('2.14.0-0'), includeMin: true);
76-
final _tripleShiftAllowed =
77-
VersionRange(min: Version.parse('2.14.0-0'), includeMin: true);
75+
final _constructorTearoffsAllowed =
76+
VersionRange(min: Version.parse('2.15.0-0'), includeMin: true);
7877

7978
// Experimental features not yet enabled by default. Move tests out of this
8079
// block when the feature is enabled by default.
8180
group('Experiments', () {
82-
group('triple-shift', () {
83-
Library tripleShift;
84-
Class C, E, F;
85-
Extension ShiftIt;
86-
Operator classShift, extensionShift;
87-
Field constantTripleShifted;
81+
group('constructor-tearoffs', () {
82+
Library constructorTearoffs;
83+
Class A, B, C, D, E, F;
84+
Mixin M;
85+
Typedef At, Bt, Ct, Et, Ft, NotAClass;
86+
Constructor Anew, Bnew, Cnew, Dnew, Enew, Fnew;
8887

8988
setUpAll(() async {
90-
tripleShift = (await _testPackageGraphExperiments)
89+
constructorTearoffs = (await _testPackageGraphExperiments)
9190
.libraries
92-
.firstWhere((l) => l.name == 'triple_shift');
93-
C = tripleShift.classes.firstWhere((c) => c.name == 'C');
94-
E = tripleShift.classes.firstWhere((c) => c.name == 'E');
95-
F = tripleShift.classes.firstWhere((c) => c.name == 'F');
96-
ShiftIt = tripleShift.extensions.firstWhere((e) => e.name == 'ShiftIt');
97-
classShift =
98-
C.instanceOperators.firstWhere((o) => o.name.contains('>>>'));
99-
extensionShift =
100-
ShiftIt.instanceOperators.firstWhere((o) => o.name.contains('>>>'));
101-
constantTripleShifted = C.constantFields
102-
.firstWhere((f) => f.name == 'constantTripleShifted');
91+
.firstWhere((l) => l.name == 'constructor_tearoffs');
92+
A = constructorTearoffs.classes.firstWhere((c) => c.name == 'A');
93+
B = constructorTearoffs.classes.firstWhere((c) => c.name == 'B');
94+
C = constructorTearoffs.classes.firstWhere((c) => c.name == 'C');
95+
D = constructorTearoffs.classes.firstWhere((c) => c.name == 'D');
96+
E = constructorTearoffs.classes.firstWhere((c) => c.name == 'E');
97+
F = constructorTearoffs.classes.firstWhere((c) => c.name == 'F');
98+
M = constructorTearoffs.mixins.firstWhere((m) => m.name == 'M');
99+
At = constructorTearoffs.typedefs.firstWhere((t) => t.name == 'At');
100+
Bt = constructorTearoffs.typedefs.firstWhere((t) => t.name == 'Bt');
101+
Ct = constructorTearoffs.typedefs.firstWhere((t) => t.name == 'Ct');
102+
Et = constructorTearoffs.typedefs.firstWhere((t) => t.name == 'Et');
103+
Ft = constructorTearoffs.typedefs.firstWhere((t) => t.name == 'Ft');
104+
NotAClass = constructorTearoffs.typedefs
105+
.firstWhere((t) => t.name == 'NotAClass');
106+
Anew = A.unnamedConstructor;
107+
Bnew = B.unnamedConstructor;
108+
Cnew = C.unnamedConstructor;
109+
Dnew = D.unnamedConstructor;
110+
Enew = E.unnamedConstructor;
111+
Fnew = F.unnamedConstructor;
103112
});
104113

105-
test('constants with triple shift render correctly', () {
106-
expect(constantTripleShifted.constantValue, equals('3 &gt;&gt;&gt; 5'));
114+
test('smoke test', () {
115+
expect(A, isNotNull);
116+
expect(B, isNotNull);
117+
expect(C, isNotNull);
118+
expect(D, isNotNull);
119+
expect(E, isNotNull);
120+
expect(F, isNotNull);
121+
expect(M, isNotNull);
122+
expect(At, isNotNull);
123+
expect(Bt, isNotNull);
124+
expect(Ct, isNotNull);
125+
expect(Et, isNotNull);
126+
expect(Ft, isNotNull);
127+
expect(NotAClass, isNotNull);
128+
expect(Anew, isNotNull);
129+
expect(Bnew, isNotNull);
130+
expect(Cnew, isNotNull);
131+
expect(Dnew, isNotNull);
132+
expect(Enew, isNotNull);
133+
expect(Fnew, isNotNull);
107134
});
108135

109-
test('operators exist and are named correctly', () {
110-
expect(classShift.name, equals('operator >>>'));
111-
expect(extensionShift.name, equals('operator >>>'));
136+
test('reference regression', () {
137+
expect(newLookup(constructorTearoffs, 'A.A'),
138+
equals(MatchingLinkResult(Anew)));
139+
expect(newLookup(constructorTearoffs, 'new A()'),
140+
equals(MatchingLinkResult(Anew)));
141+
expect(newLookup(constructorTearoffs, 'A()'),
142+
equals(MatchingLinkResult(Anew)));
143+
expect(newLookup(constructorTearoffs, 'B.B'),
144+
equals(MatchingLinkResult(Bnew)));
145+
expect(newLookup(constructorTearoffs, 'new B()'),
146+
equals(MatchingLinkResult(Bnew)));
147+
expect(newLookup(constructorTearoffs, 'B()'),
148+
equals(MatchingLinkResult(Bnew)));
149+
expect(newLookup(constructorTearoffs, 'C.C'),
150+
equals(MatchingLinkResult(Cnew)));
151+
expect(newLookup(constructorTearoffs, 'new C()'),
152+
equals(MatchingLinkResult(Cnew)));
153+
expect(newLookup(constructorTearoffs, 'C()'),
154+
equals(MatchingLinkResult(Cnew)));
155+
expect(newLookup(constructorTearoffs, 'D.D'),
156+
equals(MatchingLinkResult(Dnew)));
157+
expect(newLookup(constructorTearoffs, 'new D()'),
158+
equals(MatchingLinkResult(Dnew)));
159+
expect(newLookup(constructorTearoffs, 'D()'),
160+
equals(MatchingLinkResult(Dnew)));
161+
expect(newLookup(constructorTearoffs, 'E.E'),
162+
equals(MatchingLinkResult(Enew)));
163+
expect(newLookup(constructorTearoffs, 'new E()'),
164+
equals(MatchingLinkResult(Enew)));
165+
expect(newLookup(constructorTearoffs, 'E()'),
166+
equals(MatchingLinkResult(Enew)));
167+
expect(newLookup(constructorTearoffs, 'F.F'),
168+
equals(MatchingLinkResult(Fnew)));
169+
expect(newLookup(constructorTearoffs, 'new F()'),
170+
equals(MatchingLinkResult(Fnew)));
171+
expect(newLookup(constructorTearoffs, 'F()'),
172+
equals(MatchingLinkResult(Fnew)));
112173
});
113-
114-
test(
115-
'inheritance and overriding of triple shift operators works correctly',
116-
() {
117-
var tripleShiftE =
118-
E.instanceOperators.firstWhere((o) => o.name.contains('>>>'));
119-
var tripleShiftF =
120-
F.instanceOperators.firstWhere((o) => o.name.contains('>>>'));
121-
122-
expect(tripleShiftE.isInherited, isTrue);
123-
expect(tripleShiftE.canonicalModelElement, equals(classShift));
124-
expect(tripleShiftE.modelType.returnType.name, equals('C'));
125-
expect(tripleShiftF.isInherited, isFalse);
126-
expect(tripleShiftF.modelType.returnType.name, equals('F'));
127-
});
128-
}, skip: !_tripleShiftAllowed.allows(utils.platformVersion));
129-
130-
group('generic metadata', () {
131-
Library genericMetadata;
132-
TopLevelVariable f;
133-
Typedef F;
134-
Class C;
135-
Method mp, mn;
136-
137-
setUpAll(() async {
138-
genericMetadata = (await _testPackageGraphExperiments)
139-
.libraries
140-
.firstWhere((l) => l.name == 'generic_metadata');
141-
F = genericMetadata.typedefs.firstWhere((t) => t.name == 'F');
142-
f = genericMetadata.properties.firstWhere((p) => p.name == 'f');
143-
C = genericMetadata.classes.firstWhere((c) => c.name == 'C');
144-
mp = C.instanceMethods.firstWhere((m) => m.name == 'mp');
145-
mn = C.instanceMethods.firstWhere((m) => m.name == 'mn');
146-
});
147-
148-
test(
149-
'Verify annotations and their type arguments render on type parameters for typedefs',
150-
() {
151-
expect((F.aliasedType as FunctionType).typeFormals.first.metadata,
152-
isNotEmpty);
153-
expect((F.aliasedType as FunctionType).parameters.first.metadata,
154-
isNotEmpty);
155-
// TODO(jcollins-g): add rendering verification once we have data from
156-
// analyzer.
157-
}, skip: 'dart-lang/sdk#46064');
158-
159-
test('Verify type arguments on annotations renders, including parameters',
160-
() {
161-
var ab0 =
162-
'@<a href="%%__HTMLBASE_dartdoc_internal__%%generic_metadata/A-class.html">A</a><span class="signature">&lt;<wbr><span class="type-parameter"><a href="%%__HTMLBASE_dartdoc_internal__%%generic_metadata/B.html">B</a></span>&gt;</span>(0)';
163-
164-
expect(genericMetadata.annotations.first.linkedNameWithParameters,
165-
equals(ab0));
166-
expect(f.annotations.first.linkedNameWithParameters, equals(ab0));
167-
expect(C.annotations.first.linkedNameWithParameters, equals(ab0));
168-
expect(
169-
C.typeParameters.first.annotations.first.linkedNameWithParameters,
170-
equals(ab0));
171-
expect(
172-
mp.parameters
173-
.map((p) => p.annotations.first.linkedNameWithParameters),
174-
everyElement(equals(ab0)));
175-
expect(
176-
mn.parameters
177-
.map((p) => p.annotations.first.linkedNameWithParameters),
178-
everyElement(equals(ab0)));
179-
180-
expect(genericMetadata.features.map((f) => f.linkedNameWithParameters),
181-
contains(ab0));
182-
expect(
183-
f.features.map((f) => f.linkedNameWithParameters), contains(ab0));
184-
expect(
185-
C.features.map((f) => f.linkedNameWithParameters), contains(ab0));
186-
expect(
187-
C.typeParameters.first.features
188-
.map((f) => f.linkedNameWithParameters),
189-
contains(ab0));
190-
expect(
191-
mp.parameters
192-
.map((p) => p.features.map((f) => f.linkedNameWithParameters)),
193-
everyElement(contains(ab0)));
194-
expect(
195-
mn.parameters
196-
.map((p) => p.features.map((f) => f.linkedNameWithParameters)),
197-
everyElement(contains(ab0)));
198-
});
199-
}, skip: !_genericMetadataAllowed.allows(utils.platformVersion));
174+
}, skip: !_constructorTearoffsAllowed.allows(utils.platformVersion));
200175
});
201176

202177
group('HTML Injection when allowed', () {

0 commit comments

Comments
 (0)