Skip to content

Commit eec587e

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Migration: address a more complex case of instantiate-to-bounds.
Change-Id: I8a7d5e2e4189c3d910077ad4ec88e6c4e384dc25 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105731 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
1 parent f2cc27f commit eec587e

File tree

5 files changed

+112
-9
lines changed

5 files changed

+112
-9
lines changed

pkg/nnbd_migration/lib/src/graph_builder.dart

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -499,18 +499,27 @@ $stackTrace''');
499499
DecoratedType visitTypeName(TypeName typeName) {
500500
var typeArguments = typeName.typeArguments?.arguments;
501501
var element = typeName.name.staticElement;
502-
if (typeArguments != null) {
503-
for (int i = 0; i < typeArguments.length; i++) {
504-
DecoratedType bound;
505-
if (element is TypeParameterizedElement) {
502+
if (element is TypeParameterizedElement) {
503+
if (typeArguments == null) {
504+
var instantiatedType =
505+
_variables.decoratedTypeAnnotation(_source, typeName);
506+
for (int i = 0; i < instantiatedType.typeArguments.length; i++) {
507+
_unionDecoratedTypes(
508+
instantiatedType.typeArguments[0],
509+
_variables.decoratedElementType(element.typeParameters[i],
510+
create: true));
511+
}
512+
} else {
513+
for (int i = 0; i < typeArguments.length; i++) {
514+
DecoratedType bound;
506515
bound = _variables.decoratedElementType(element.typeParameters[i],
507516
create: true);
508-
} else {
509-
throw new UnimplementedError('TODO(paulberry)');
517+
_checkAssignment(
518+
bound,
519+
_variables.decoratedTypeAnnotation(_source, typeArguments[i]),
520+
null,
521+
hard: true);
510522
}
511-
_checkAssignment(bound,
512-
_variables.decoratedTypeAnnotation(_source, typeArguments[i]), null,
513-
hard: true);
514523
}
515524
}
516525
return DecoratedType(typeName.type, _graph.never);
@@ -669,6 +678,20 @@ $stackTrace''');
669678
}
670679
return false;
671680
}
681+
682+
void _unionDecoratedTypes(DecoratedType x, DecoratedType y) {
683+
_graph.union(x.node, y.node);
684+
if (x.typeArguments.isNotEmpty ||
685+
y.typeArguments.isNotEmpty ||
686+
x.returnType != null ||
687+
y.returnType != null ||
688+
x.positionalParameters.isNotEmpty ||
689+
y.positionalParameters.isNotEmpty ||
690+
x.namedParameters.isNotEmpty ||
691+
y.namedParameters.isNotEmpty) {
692+
throw UnimplementedError('TODO(paulberry): _unionDecoratedTypes($x, $y)');
693+
}
694+
}
672695
}
673696

674697
/// Information about a binary expression whose boolean value could possibly

pkg/nnbd_migration/lib/src/node_builder.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ $stackTrace''');
237237
DecoratedType _decorateImplicitTypeArgument(DartType type) {
238238
if (type.isDynamic) {
239239
return DecoratedType(type, _graph.always);
240+
} else if (type is InterfaceType) {
241+
return DecoratedType(type, NullabilityNode.forInferredType(),
242+
typeArguments:
243+
type.typeArguments.map(_decorateImplicitTypeArgument).toList());
240244
}
241245
throw UnimplementedError('TODO(paulberry): ${type.runtimeType}');
242246
}

pkg/nnbd_migration/lib/src/nullability_node.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ abstract class NullabilityNode {
273273
return node;
274274
}
275275

276+
/// Creates a [NullabilityNode] representing the nullability of a variable
277+
/// whose type is determined by type inference.
278+
factory NullabilityNode.forInferredType() =>
279+
_NullabilityNodeSimple('inferred');
280+
276281
/// Creates a [NullabilityNode] representing the nullability of an
277282
/// expression which is nullable iff both [a] and [b] are nullable.
278283
///

pkg/nnbd_migration/test/api_test.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,33 @@ class C<E> {}
657657
await _checkSingleFileChanges(content, expected);
658658
}
659659

660+
test_genericType_noTypeArguments_use_bound() async {
661+
var content = '''
662+
abstract class C<T extends Object> { // (1)
663+
void put(T t);
664+
T get();
665+
}
666+
Object f(C c) => c.get(); // (2)
667+
void g(C<int> c) { // (3)
668+
c.put(null); // (4)
669+
}
670+
''';
671+
// (4) forces `...C<int?>...` at (3), which means (1) must be
672+
// `...extends Object?`. Therefore (2) is equivalent to
673+
// `...f(C<Object?> c)...`, so the return type of `f` is `Object?`.
674+
var expected = '''
675+
abstract class C<T extends Object?> { // (1)
676+
void put(T t);
677+
T get();
678+
}
679+
Object? f(C c) => c.get(); // (2)
680+
void g(C<int?> c) { // (3)
681+
c.put(null); // (4)
682+
}
683+
''';
684+
await _checkSingleFileChanges(content, expected);
685+
}
686+
660687
test_getter_topLevel() async {
661688
var content = '''
662689
int get g => 0;

pkg/nnbd_migration/test/migration_visitor_test.dart

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,16 @@ Type f() {
11621162
assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node);
11631163
}
11641164

1165+
test_typeName_union_with_bound() async {
1166+
await analyze('''
1167+
class C<T extends Object> {}
1168+
void f(C c) {}
1169+
''');
1170+
var cType = decoratedTypeAnnotation('C c');
1171+
var cBound = decoratedTypeAnnotation('Object');
1172+
assertUnion(cType.typeArguments[0].node, cBound.node);
1173+
}
1174+
11651175
test_variableDeclaration() async {
11661176
await analyze('''
11671177
void f(int i) {
@@ -1352,6 +1362,40 @@ void f(List x) {}
13521362
expect(decoratedArgType.node, same(always));
13531363
}
13541364

1365+
test_interfaceType_generic_instantiate_to_generic_type() async {
1366+
await analyze('''
1367+
class C<T> {}
1368+
class D<T extends C<int>> {}
1369+
void f(D x) {}
1370+
''');
1371+
var decoratedListType = decoratedTypeAnnotation('D x');
1372+
expect(decoratedFunctionType('f').positionalParameters[0],
1373+
same(decoratedListType));
1374+
expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
1375+
expect(decoratedListType.typeArguments, hasLength(1));
1376+
var decoratedArgType = decoratedListType.typeArguments[0];
1377+
expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
1378+
expect(decoratedArgType.typeArguments, hasLength(1));
1379+
var decoratedArgArgType = decoratedArgType.typeArguments[0];
1380+
expect(decoratedArgArgType.node, TypeMatcher<NullabilityNodeMutable>());
1381+
expect(decoratedArgArgType.typeArguments, isEmpty);
1382+
}
1383+
1384+
test_interfaceType_generic_instantiate_to_object() async {
1385+
await analyze('''
1386+
class C<T extends Object> {}
1387+
void f(C x) {}
1388+
''');
1389+
var decoratedListType = decoratedTypeAnnotation('C x');
1390+
expect(decoratedFunctionType('f').positionalParameters[0],
1391+
same(decoratedListType));
1392+
expect(decoratedListType.node, TypeMatcher<NullabilityNodeMutable>());
1393+
expect(decoratedListType.typeArguments, hasLength(1));
1394+
var decoratedArgType = decoratedListType.typeArguments[0];
1395+
expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
1396+
expect(decoratedArgType.typeArguments, isEmpty);
1397+
}
1398+
13551399
test_interfaceType_typeParameter() async {
13561400
await analyze('''
13571401
void f(List<int> x) {}

0 commit comments

Comments
 (0)