Skip to content

Commit a200980

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Migration: add support for variable and field type inference.
Change-Id: Iae66992d4f9734438776af34b68aabf20f5c15d0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107517 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
1 parent ca25f56 commit a200980

File tree

6 files changed

+227
-10
lines changed

6 files changed

+227
-10
lines changed

pkg/nnbd_migration/lib/src/edge_builder.dart

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -849,14 +849,26 @@ $stackTrace''');
849849
}
850850

851851
@override
852-
DecoratedType visitVariableDeclaration(VariableDeclaration node) {
853-
var destinationType = getOrComputeElementType(node.declaredElement);
854-
var initializer = node.initializer;
855-
if (initializer == null) {
856-
// TODO(paulberry)
857-
_unimplemented(node, 'Variable declaration with no initializer');
858-
} else {
859-
_handleAssignment(initializer, destinationType);
852+
DecoratedType visitVariableDeclarationList(VariableDeclarationList node) {
853+
node.metadata.accept(this);
854+
var typeAnnotation = node.type;
855+
for (var variable in node.variables) {
856+
variable.metadata.accept(this);
857+
var initializer = variable.initializer;
858+
if (initializer != null) {
859+
var destinationType = getOrComputeElementType(variable.declaredElement);
860+
if (typeAnnotation == null) {
861+
var initializerType = initializer.accept(this);
862+
if (initializerType == null) {
863+
throw StateError('No type computed for ${initializer.runtimeType} '
864+
'(${initializer.toSource()}) offset=${initializer.offset}');
865+
}
866+
_unionDecoratedTypes(initializerType, destinationType,
867+
InitializerInferenceOrigin(_source, variable.name.offset));
868+
} else {
869+
_handleAssignment(initializer, destinationType);
870+
}
871+
}
860872
}
861873
return null;
862874
}

pkg/nnbd_migration/lib/src/edge_origin.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ class InheritanceOrigin extends EdgeOriginWithLocation {
6262
InheritanceOrigin(Source source, int offset) : super(source, offset);
6363
}
6464

65+
/// Edge origin resulting from a type that is inferred from its initializer.
66+
class InitializerInferenceOrigin extends EdgeOriginWithLocation {
67+
InitializerInferenceOrigin(Source source, int offset) : super(source, offset);
68+
}
69+
6570
/// Edge origin resulting from a class that is instantiated to bounds.
6671
///
6772
/// For example, in the following code snippet:

pkg/nnbd_migration/lib/src/node_builder.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,13 @@ $stackTrace''');
344344
@override
345345
DecoratedType visitVariableDeclarationList(VariableDeclarationList node) {
346346
node.metadata.accept(this);
347-
var type = decorateType(node.type, node);
347+
var typeAnnotation = node.type;
348+
var type = typeAnnotation?.accept(this);
348349
for (var variable in node.variables) {
349350
variable.metadata.accept(this);
350-
_variables.recordDecoratedElementType(variable.declaredElement, type);
351+
var declaredElement = variable.declaredElement;
352+
_variables.recordDecoratedElementType(declaredElement,
353+
type ?? DecoratedType.forImplicitType(declaredElement.type, _graph));
351354
variable.initializer?.accept(this);
352355
}
353356
return null;

pkg/nnbd_migration/test/api_test.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,31 @@ int f(int i) {
813813
await _checkSingleFileChanges(content, expected);
814814
}
815815

816+
test_field_type_inferred() async {
817+
var content = '''
818+
int f() => null;
819+
class C {
820+
var x = 1;
821+
void g() {
822+
x = f();
823+
}
824+
}
825+
''';
826+
// The type of x is inferred from its initializer, so it is non-nullable,
827+
// even though we try to assign a nullable value to it. So a null check
828+
// must be added.
829+
var expected = '''
830+
int? f() => null;
831+
class C {
832+
var x = 1;
833+
void g() {
834+
x = f()!;
835+
}
836+
}
837+
''';
838+
await _checkSingleFileChanges(content, expected);
839+
}
840+
816841
test_genericType_noTypeArguments() async {
817842
var content = '''
818843
void f(C c) {}
@@ -1042,6 +1067,27 @@ class C {
10421067
await _checkSingleFileChanges(content, expected);
10431068
}
10441069

1070+
test_localVariable_type_inferred() async {
1071+
var content = '''
1072+
int f() => null;
1073+
void main() {
1074+
var x = 1;
1075+
x = f();
1076+
}
1077+
''';
1078+
// The type of x is inferred from its initializer, so it is non-nullable,
1079+
// even though we try to assign a nullable value to it. So a null check
1080+
// must be added.
1081+
var expected = '''
1082+
int? f() => null;
1083+
void main() {
1084+
var x = 1;
1085+
x = f()!;
1086+
}
1087+
''';
1088+
await _checkSingleFileChanges(content, expected);
1089+
}
1090+
10451091
test_named_parameter_no_default_unused() async {
10461092
var content = '''
10471093
void f({String s}) {}
@@ -1613,6 +1659,27 @@ Object? g() => f();
16131659
await _checkSingleFileChanges(content, expected);
16141660
}
16151661

1662+
test_topLevelVariable_type_inferred() async {
1663+
var content = '''
1664+
int f() => null;
1665+
var x = 1;
1666+
void main() {
1667+
x = f();
1668+
}
1669+
''';
1670+
// The type of x is inferred from its initializer, so it is non-nullable,
1671+
// even though we try to assign a nullable value to it. So a null check
1672+
// must be added.
1673+
var expected = '''
1674+
int? f() => null;
1675+
var x = 1;
1676+
void main() {
1677+
x = f()!;
1678+
}
1679+
''';
1680+
await _checkSingleFileChanges(content, expected);
1681+
}
1682+
16161683
test_two_files() async {
16171684
var root = '/home/test/lib';
16181685
var path1 = convertPath('$root/file1.dart');

pkg/nnbd_migration/test/edge_builder_test.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,18 @@ double f() {
799799
assertNoUpstreamNullability(decoratedTypeAnnotation('double').node);
800800
}
801801

802+
test_field_type_inferred() async {
803+
await analyze('''
804+
int f() => 1;
805+
class C {
806+
var x = f();
807+
}
808+
''');
809+
var xType =
810+
variables.decoratedElementType(findNode.simple('x').staticElement);
811+
assertUnion(xType.node, decoratedTypeAnnotation('int').node);
812+
}
813+
802814
test_functionDeclaration_expression_body() async {
803815
await analyze('''
804816
int/*1*/ f(int/*2*/ i) => i/*3*/;
@@ -1310,6 +1322,18 @@ List<String> f() {
13101322
assertEdge(always, decoratedTypeAnnotation('String>[').node, hard: false);
13111323
}
13121324

1325+
test_localVariable_type_inferred() async {
1326+
await analyze('''
1327+
int f() => 1;
1328+
main() {
1329+
var x = f();
1330+
}
1331+
''');
1332+
var xType =
1333+
variables.decoratedElementType(findNode.simple('x').staticElement);
1334+
assertUnion(xType.node, decoratedTypeAnnotation('int').node);
1335+
}
1336+
13131337
test_method_parameterType_inferred() async {
13141338
await analyze('''
13151339
class B {
@@ -2329,6 +2353,16 @@ double get myPi => pi;
23292353
assertEdge(never, myPiType.node, hard: false);
23302354
}
23312355

2356+
test_topLevelVariable_type_inferred() async {
2357+
await analyze('''
2358+
int f() => 1;
2359+
var x = f();
2360+
''');
2361+
var xType =
2362+
variables.decoratedElementType(findNode.simple('x').staticElement);
2363+
assertUnion(xType.node, decoratedTypeAnnotation('int').node);
2364+
}
2365+
23322366
test_type_argument_explicit_bound() async {
23332367
await analyze('''
23342368
class C<T extends Object> {}

pkg/nnbd_migration/test/node_builder_test.dart

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,40 @@ dynamic f() {}
367367
assertEdge(always, decoratedType.node, hard: false);
368368
}
369369

370+
test_field_type_implicit_dynamic() async {
371+
await analyze('''
372+
class C {
373+
var x;
374+
}
375+
''');
376+
var decoratedType =
377+
variables.decoratedElementType(findNode.simple('x').staticElement);
378+
expect(decoratedType.node, same(always));
379+
}
380+
381+
test_field_type_inferred() async {
382+
await analyze('''
383+
class C {
384+
var x = 1;
385+
}
386+
''');
387+
var decoratedType =
388+
variables.decoratedElementType(findNode.simple('x').staticElement);
389+
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
390+
}
391+
392+
test_field_type_inferred_dynamic() async {
393+
await analyze('''
394+
dynamic f() {}
395+
class C {
396+
var x = f();
397+
}
398+
''');
399+
var decoratedType =
400+
variables.decoratedElementType(findNode.simple('x').staticElement);
401+
expect(decoratedType.node, same(always));
402+
}
403+
370404
test_field_type_simple() async {
371405
await analyze('''
372406
class C {
@@ -555,6 +589,40 @@ void f(List<int> x) {}
555589
expect(decoratedIntType.node, isNot(never));
556590
}
557591

592+
test_localVariable_type_implicit_dynamic() async {
593+
await analyze('''
594+
main() {
595+
var x;
596+
}
597+
''');
598+
var decoratedType =
599+
variables.decoratedElementType(findNode.simple('x').staticElement);
600+
expect(decoratedType.node, same(always));
601+
}
602+
603+
test_localVariable_type_inferred() async {
604+
await analyze('''
605+
main() {
606+
var x = 1;
607+
}
608+
''');
609+
var decoratedType =
610+
variables.decoratedElementType(findNode.simple('x').staticElement);
611+
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
612+
}
613+
614+
test_localVariable_type_inferred_dynamic() async {
615+
await analyze('''
616+
dynamic f() {}
617+
main() {
618+
var x = f();
619+
}
620+
''');
621+
var decoratedType =
622+
variables.decoratedElementType(findNode.simple('x').staticElement);
623+
expect(decoratedType.node, same(always));
624+
}
625+
558626
test_method_parameterType_implicit_dynamic() async {
559627
await analyze('''
560628
class C {
@@ -754,6 +822,34 @@ int f() => 0;
754822
expect(decoratedType.node, isNot(never));
755823
}
756824

825+
test_topLevelVariable_type_implicit_dynamic() async {
826+
await analyze('''
827+
var x;
828+
''');
829+
var decoratedType =
830+
variables.decoratedElementType(findNode.simple('x').staticElement);
831+
expect(decoratedType.node, same(always));
832+
}
833+
834+
test_topLevelVariable_type_inferred() async {
835+
await analyze('''
836+
var x = 1;
837+
''');
838+
var decoratedType =
839+
variables.decoratedElementType(findNode.simple('x').staticElement);
840+
expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
841+
}
842+
843+
test_topLevelVariable_type_inferred_dynamic() async {
844+
await analyze('''
845+
dynamic f() {}
846+
var x = f();
847+
''');
848+
var decoratedType =
849+
variables.decoratedElementType(findNode.simple('x').staticElement);
850+
expect(decoratedType.node, same(always));
851+
}
852+
757853
test_type_comment_bang() async {
758854
await analyze('''
759855
void f(int/*!*/ i) {}

0 commit comments

Comments
 (0)