Migration: Add IndexExpression support to FixBuilder.
Change-Id: I870047d12304c70cfe89e64d4864cc1809b7e4ac
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121408
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 6322c06..a9fd486 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -179,6 +179,34 @@
? writeType
: _computeMigratedType(auxiliaryElements.staticElement);
return AssignmentTargetInfo(isCompound ? readType : null, writeType);
+ } else if (node is IndexExpression) {
+ var targetType =
+ visitSubexpression(node.target, _typeProvider.objectType);
+ var writeElement = node.staticElement;
+ DartType indexContext;
+ DartType writeType;
+ DartType readType;
+ if (writeElement == null) {
+ indexContext = UnknownInferredType.instance;
+ writeType = _typeProvider.dynamicType;
+ readType = isCompound ? _typeProvider.dynamicType : null;
+ } else {
+ var writerType =
+ _computeMigratedType(writeElement, targetType: targetType)
+ as FunctionType;
+ writeType = writerType.parameters[1].type;
+ if (isCompound) {
+ var readerType = _computeMigratedType(
+ node.auxiliaryElements.staticElement,
+ targetType: targetType) as FunctionType;
+ readType = readerType.returnType;
+ indexContext = readerType.parameters[0].type;
+ } else {
+ indexContext = writerType.parameters[0].type;
+ }
+ }
+ visitSubexpression(node.index, indexContext);
+ return AssignmentTargetInfo(readType, writeType);
} else {
throw UnimplementedError('TODO(paulberry)');
}
@@ -277,6 +305,28 @@
}
@override
+ DartType visitIndexExpression(IndexExpression node) {
+ var target = node.target;
+ var staticElement = node.staticElement;
+ var index = node.index;
+ var targetType = visitSubexpression(target, _typeProvider.objectType);
+ DartType contextType;
+ DartType returnType;
+ if (staticElement == null) {
+ contextType = _typeProvider.dynamicType;
+ returnType = _typeProvider.dynamicType;
+ } else {
+ var methodType =
+ _computeMigratedType(staticElement, targetType: targetType)
+ as FunctionType;
+ contextType = methodType.parameters[0].type;
+ returnType = methodType.returnType;
+ }
+ visitSubexpression(index, contextType);
+ return returnType;
+ }
+
+ @override
DartType visitListLiteral(ListLiteral node) {
DartType contextType;
var typeArguments = node.typeArguments;
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index 143c4a7..251a3805 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -236,6 +236,158 @@
visitSubexpression(findNode.binary('&&'), 'bool');
}
+ test_assignmentTarget_indexExpression_compound_dynamic() async {
+ await analyze('''
+_f(dynamic d, int/*?*/ i) => d[i] += 0;
+''');
+ visitAssignmentTarget(findNode.index('d[i]'), 'dynamic', 'dynamic');
+ }
+
+ test_assignmentTarget_indexExpression_compound_simple() async {
+ await analyze('''
+class _C {
+ int operator[](String s) => 1;
+ void operator[]=(String s, num n) {}
+}
+_f(_C c) => c['foo'] += 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), 'int', 'num');
+ }
+
+ test_assignmentTarget_indexExpression_compound_simple_check_lhs() async {
+ await analyze('''
+class _C {
+ int operator[](String s) => 1;
+ void operator[]=(String s, num n) {}
+}
+_f(_C/*?*/ c) => c['foo'] += 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), 'int', 'num',
+ nullChecked: {findNode.simple('c[')});
+ }
+
+ test_assignmentTarget_indexExpression_compound_simple_check_rhs() async {
+ await analyze('''
+class _C {
+ int operator[](String/*!*/ s) => 1;
+ void operator[]=(String/*?*/ s, num n) {}
+}
+_f(_C c, String/*?*/ s) => c[s] += 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), 'int', 'num',
+ nullChecked: {findNode.simple('s]')});
+ }
+
+ test_assignmentTarget_indexExpression_compound_substituted() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+ void operator[]=(U u, T t) {}
+}
+_f(_C<int, String> c) => c['foo'] += 1;
+''');
+ visitAssignmentTarget(findNode.index('c['), 'int', 'int');
+ }
+
+ test_assignmentTarget_indexExpression_compound_substituted_check_rhs() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+ void operator[]=(U/*?*/ u, T t) {}
+}
+_f(_C<int, String/*!*/> c, String/*?*/ s) => c[s] += 1;
+''');
+ visitAssignmentTarget(findNode.index('c['), 'int', 'int',
+ nullChecked: {findNode.simple('s]')});
+ }
+
+ test_assignmentTarget_indexExpression_compound_substituted_no_check_rhs() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+ void operator[]=(U u, T t) {}
+}
+_f(_C<int, String/*?*/> c, String/*?*/ s) => c[s] += 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), 'int', 'int');
+ }
+
+ test_assignmentTarget_indexExpression_dynamic() async {
+ await analyze('''
+_f(dynamic d, int/*?*/ i) => d[i] = 0;
+''');
+ visitAssignmentTarget(findNode.index('d[i]'), null, 'dynamic');
+ }
+
+ test_assignmentTarget_indexExpression_simple() async {
+ await analyze('''
+class _C {
+ int operator[](String s) => 1;
+ void operator[]=(String s, num n) {}
+}
+_f(_C c) => c['foo'] = 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), null, 'num');
+ }
+
+ test_assignmentTarget_indexExpression_simple_check_lhs() async {
+ await analyze('''
+class _C {
+ int operator[](String s) => 1;
+ void operator[]=(String s, num n) {}
+}
+_f(_C/*?*/ c) => c['foo'] = 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), null, 'num',
+ nullChecked: {findNode.simple('c[')});
+ }
+
+ test_assignmentTarget_indexExpression_simple_check_rhs() async {
+ await analyze('''
+class _C {
+ int operator[](String/*?*/ s) => 1;
+ void operator[]=(String/*!*/ s, num n) {}
+}
+_f(_C c, String/*?*/ s) => c[s] = 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), null, 'num',
+ nullChecked: {findNode.simple('s]')});
+ }
+
+ test_assignmentTarget_indexExpression_substituted() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+ void operator[]=(U u, T t) {}
+}
+_f(_C<int, String> c) => c['foo'] = 1;
+''');
+ visitAssignmentTarget(findNode.index('c['), null, 'int');
+ }
+
+ test_assignmentTarget_indexExpression_substituted_check_rhs() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+ void operator[]=(U/*?*/ u, T t) {}
+}
+_f(_C<int, String/*!*/> c, String/*?*/ s) => c[s] = 1;
+''');
+ visitAssignmentTarget(findNode.index('c['), null, 'int',
+ nullChecked: {findNode.simple('s]')});
+ }
+
+ test_assignmentTarget_indexExpression_substituted_no_check_rhs() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+ void operator[]=(U u, T t) {}
+}
+_f(_C<int, String/*?*/> c, String/*?*/ s) => c[s] = 0;
+''');
+ visitAssignmentTarget(findNode.index('c['), null, 'int');
+ }
+
test_assignmentTarget_simpleIdentifier_field_generic() async {
await analyze('''
abstract class _C<T> {
@@ -645,6 +797,77 @@
visitStatement(findNode.statement('if'));
}
+ test_indexExpression_dynamic() async {
+ await analyze('''
+Object/*!*/ _f(dynamic d, int/*?*/ i) => d[i];
+''');
+ visitSubexpression(findNode.index('d[i]'), 'dynamic',
+ contextType: objectType);
+ }
+
+ test_indexExpression_simple() async {
+ await analyze('''
+class _C {
+ int operator[](String s) => 1;
+}
+_f(_C c) => c['foo'];
+''');
+ visitSubexpression(findNode.index('c['), 'int');
+ }
+
+ test_indexExpression_simple_check_lhs() async {
+ await analyze('''
+class _C {
+ int operator[](String s) => 1;
+}
+_f(_C/*?*/ c) => c['foo'];
+''');
+ visitSubexpression(findNode.index('c['), 'int',
+ nullChecked: {findNode.simple('c[')});
+ }
+
+ test_indexExpression_simple_check_rhs() async {
+ await analyze('''
+class _C {
+ int operator[](String/*!*/ s) => 1;
+}
+_f(_C c, String/*?*/ s) => c[s];
+''');
+ visitSubexpression(findNode.index('c['), 'int',
+ nullChecked: {findNode.simple('s]')});
+ }
+
+ test_indexExpression_substituted() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+}
+_f(_C<int, String> c) => c['foo'];
+''');
+ visitSubexpression(findNode.index('c['), 'int');
+ }
+
+ test_indexExpression_substituted_check_rhs() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+}
+_f(_C<int, String/*!*/> c, String/*?*/ s) => c[s];
+''');
+ visitSubexpression(findNode.index('c['), 'int',
+ nullChecked: {findNode.simple('s]')});
+ }
+
+ test_indexExpression_substituted_no_check_rhs() async {
+ await analyze('''
+class _C<T, U> {
+ T operator[](U u) => throw 'foo';
+}
+_f(_C<int, String/*?*/> c, String/*?*/ s) => c[s];
+''');
+ visitSubexpression(findNode.index('c['), 'int');
+ }
+
test_integerLiteral() async {
await analyze('''
f() => 1;