Skip to content

Commit b5e341e

Browse files
authored
Fix problem with unbound type parameters as extension targets (#2129)
* Fix problem with unbound type parameters as extension targets * Eliminate type related deprecation warnings and fix bugs in changed type hierarchy * Remove TODO that was implemented * Revert accidentally included change from #2130 * Review comments
1 parent 465f1dc commit b5e341e

File tree

4 files changed

+70
-36
lines changed

4 files changed

+70
-36
lines changed

lib/src/element_type.dart

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,17 @@ abstract class ElementType extends Privacy {
7171

7272
String get linkedName;
7373

74-
String get name => type.name ?? type.element.name;
74+
String get name;
7575

7676
String get nameWithGenerics;
7777

7878
List<Parameter> get parameters => [];
7979

80+
DartType get instantiatedType;
81+
82+
bool isBoundSupertypeTo(ElementType t);
83+
bool isSubtypeOf(ElementType t);
84+
8085
@override
8186
String toString() => "$type";
8287

@@ -95,23 +100,37 @@ class UndefinedElementType extends ElementType {
95100
@override
96101
bool get isPublic => true;
97102

103+
@override
104+
String get name {
105+
if (type.isDynamic) {
106+
if (returnedFrom != null &&
107+
(returnedFrom is DefinedElementType &&
108+
(returnedFrom as DefinedElementType).element.isAsynchronous)) {
109+
return 'Future';
110+
} else {
111+
return 'dynamic';
112+
}
113+
}
114+
if (type.isVoid) return 'void';
115+
assert(false, 'Unrecognized type for UndefinedElementType');
116+
return '';
117+
}
118+
98119
@override
99120
String get nameWithGenerics => name;
100121

101-
/// dynamic and void are not allowed to have parameterized types.
122+
/// Assume that undefined elements don't have useful bounds.
102123
@override
103-
String get linkedName {
104-
if (type.isDynamic &&
105-
returnedFrom != null &&
106-
(returnedFrom is DefinedElementType &&
107-
(returnedFrom as DefinedElementType).element.isAsynchronous)) {
108-
return 'Future';
109-
}
110-
return name;
111-
}
124+
DartType get instantiatedType => type;
125+
126+
@override
127+
bool isBoundSupertypeTo(ElementType t) => false;
112128

113129
@override
114-
String get name => type.name ?? '';
130+
bool isSubtypeOf(ElementType t) => type.isBottom && !t.type.isBottom;
131+
132+
@override
133+
String get linkedName => name;
115134
}
116135

117136
/// A FunctionType that does not have an underpinning Element.
@@ -209,8 +228,7 @@ class TypeParameterElementType extends DefinedElementType {
209228
ClassElement get _boundClassElement => type.element;
210229

211230
@override
212-
// TODO(jcollins-g): This is wrong; bound is not always an InterfaceType.
213-
InterfaceType get _interfaceType => (type as TypeParameterType).bound;
231+
DartType get _bound => (type as TypeParameterType).bound;
214232
}
215233

216234
/// An [ElementType] associated with an [Element].
@@ -226,6 +244,9 @@ abstract class DefinedElementType extends ElementType {
226244
return _element;
227245
}
228246

247+
@override
248+
String get name => type.element.name;
249+
229250
bool get isParameterType => (type is TypeParameterType);
230251

231252
/// This type is a public type if the underlying, canonical element is public.
@@ -274,32 +295,40 @@ abstract class DefinedElementType extends ElementType {
274295
Class get boundClass =>
275296
ModelElement.fromElement(_boundClassElement, packageGraph);
276297

277-
InterfaceType get _interfaceType => type;
298+
DartType get _bound => type;
278299

279-
InterfaceType _instantiatedType;
300+
DartType _instantiatedType;
280301

281302
/// Return this type, instantiated to bounds if it isn't already.
303+
@override
282304
DartType get instantiatedType {
283305
if (_instantiatedType == null) {
284-
if (!_interfaceType.typeArguments.every((t) => t is InterfaceType)) {
306+
if (_bound is InterfaceType &&
307+
!(_bound as InterfaceType)
308+
.typeArguments
309+
.every((t) => t is InterfaceType)) {
285310
var typeSystem = library.element.typeSystem as TypeSystemImpl;
286-
_instantiatedType = typeSystem.instantiateToBounds(_interfaceType);
311+
// TODO(jcollins-g): convert to ClassElement.instantiateToBounds
312+
// dart-lang/dartdoc#2135
313+
_instantiatedType = typeSystem.instantiateToBounds(_bound);
287314
} else {
288-
_instantiatedType = _interfaceType;
315+
_instantiatedType = _bound;
289316
}
290317
}
291318
return _instantiatedType;
292319
}
293320

294321
/// The instantiated to bounds type of this type is a subtype of
295322
/// [t].
296-
bool isSubtypeOf(DefinedElementType t) =>
323+
@override
324+
bool isSubtypeOf(ElementType t) =>
297325
library.typeSystem.isSubtypeOf(instantiatedType, t.instantiatedType);
298326

299327
/// Returns true if at least one supertype (including via mixins and
300328
/// interfaces) is equivalent to or a subtype of [this] when
301329
/// instantiated to bounds.
302-
bool isBoundSupertypeTo(DefinedElementType t) =>
330+
@override
331+
bool isBoundSupertypeTo(ElementType t) =>
303332
_isBoundSupertypeTo(t.instantiatedType, HashSet());
304333

305334
bool _isBoundSupertypeTo(DartType superType, HashSet<DartType> visited) {

lib/src/model/extension.dart

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,23 @@ class Extension extends Container
2323

2424
/// Detect if this extension applies to every object.
2525
bool get alwaysApplies =>
26-
extendedType.type.isDynamic ||
27-
extendedType.type.isVoid ||
28-
extendedType.type.isObject;
26+
extendedType.instantiatedType.isDynamic ||
27+
extendedType.instantiatedType.isVoid ||
28+
extendedType.instantiatedType.isObject;
2929

3030
bool couldApplyTo<T extends ExtensionTarget>(T c) =>
3131
_couldApplyTo(c.modelType);
3232

3333
/// Return true if this extension could apply to [t].
3434
bool _couldApplyTo(DefinedElementType t) {
35-
if (extendedType is UndefinedElementType) {
36-
assert(extendedType.type.isDynamic || extendedType.type.isVoid);
35+
if (extendedType.instantiatedType.isDynamic ||
36+
extendedType.instantiatedType.isVoid) {
3737
return true;
3838
}
39-
{
40-
DefinedElementType extendedType = this.extendedType;
41-
return t.instantiatedType == extendedType.instantiatedType ||
42-
(t.instantiatedType.element ==
43-
extendedType.instantiatedType.element &&
44-
extendedType.isSubtypeOf(t)) ||
45-
extendedType.isBoundSupertypeTo(t);
46-
}
39+
return t.instantiatedType == extendedType.instantiatedType ||
40+
(t.instantiatedType.element == extendedType.instantiatedType.element &&
41+
extendedType.isSubtypeOf(t)) ||
42+
extendedType.isBoundSupertypeTo(t);
4743
}
4844

4945
@override

test/model_test.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1867,22 +1867,28 @@ void main() {
18671867
});
18681868

18691869
test('extensions on special types work', () {
1870-
Extension extensionOnDynamic, extensionOnVoid, extensionOnNull;
1870+
Extension extensionOnDynamic,
1871+
extensionOnVoid,
1872+
extensionOnNull,
1873+
extensionOnTypeParameter;
18711874
Class object = packageGraph.specialClasses[SpecialClass.object];
18721875
Extension getExtension(String name) =>
18731876
fakeLibrary.extensions.firstWhere((e) => e.name == name);
18741877

18751878
extensionOnDynamic = getExtension('ExtensionOnDynamic');
18761879
extensionOnNull = getExtension('ExtensionOnNull');
18771880
extensionOnVoid = getExtension('ExtensionOnVoid');
1881+
extensionOnTypeParameter = getExtension('ExtensionOnTypeParameter');
18781882

18791883
expect(extensionOnDynamic.couldApplyTo(object), isTrue);
18801884
expect(extensionOnVoid.couldApplyTo(object), isTrue);
18811885
expect(extensionOnNull.couldApplyTo(object), isFalse);
1886+
expect(extensionOnTypeParameter.couldApplyTo(object), isTrue);
18821887

18831888
expect(extensionOnDynamic.alwaysApplies, isTrue);
18841889
expect(extensionOnVoid.alwaysApplies, isTrue);
18851890
expect(extensionOnNull.alwaysApplies, isFalse);
1891+
expect(extensionOnTypeParameter.alwaysApplies, isTrue);
18861892

18871893
// Even though it does have extensions that could apply to it,
18881894
// extensions that apply to [Object] should always be hidden from

testing/test_package/lib/fake.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,6 @@ extension DoSomething2X<A, B, R> on Function1<A, Function1<B, R>> {
11371137
Function2<A, B, R> something() => (A first, B second) => this(first)(second);
11381138
}
11391139

1140-
11411140
/// Extensions might exist on types defined by the language.
11421141
extension ExtensionOnDynamic on dynamic {
11431142
void youCanAlwaysCallMe() {}
@@ -1151,3 +1150,7 @@ extension ExtensionOnNull on Null {
11511150
void youCanOnlyCallMeOnNulls() {}
11521151
}
11531152

1153+
/// Extensions might exist on unbound type parameters.
1154+
extension ExtensionOnTypeParameter<T> on T {
1155+
T aFunctionReturningT(T other) => other;
1156+
}

0 commit comments

Comments
 (0)