0e8a84173e
Previously, if the migration tool encountered a Future expression with a bad type (e.g. a Future<String?> where a Future<String> was needed), it would "fix" the problem by introducing a cast. That is nearly always the wrong thing to do; what we want to do is null check the value that the future *completes* with. This CL changes the migration tool so that it fixes this case by appending `.then((value) => value!)` to the future expression. Fixes #45472. Bug: https://github.com/dart-lang/sdk/issues/45472 Change-Id: I7a35b54f673936e2e4b0f8f3a077ba8bf684b4eb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193700 Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
2119 lines
66 KiB
Dart
2119 lines
66 KiB
Dart
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'package:analyzer/dart/ast/ast.dart';
|
|
import 'package:analyzer/dart/element/nullability_suffix.dart';
|
|
import 'package:analyzer/dart/element/type.dart';
|
|
import 'package:analyzer/src/dart/element/element.dart';
|
|
import 'package:analyzer/src/dart/element/type.dart';
|
|
import 'package:analyzer/src/dart/element/type_provider.dart';
|
|
import 'package:analyzer/src/generated/utilities_dart.dart';
|
|
import 'package:nnbd_migration/nnbd_migration.dart';
|
|
import 'package:nnbd_migration/src/decorated_type.dart';
|
|
import 'package:nnbd_migration/src/edit_plan.dart';
|
|
import 'package:nnbd_migration/src/fix_aggregator.dart';
|
|
import 'package:nnbd_migration/src/nullability_node.dart';
|
|
import 'package:nnbd_migration/src/nullability_node_target.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import 'abstract_single_unit.dart';
|
|
|
|
main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(FixAggregatorTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class FixAggregatorTest extends FixAggregatorTestBase {
|
|
TypeProviderImpl get nnbdTypeProvider =>
|
|
(testAnalysisResult.typeProvider as TypeProviderImpl)
|
|
.asNonNullableByDefault;
|
|
|
|
Future<void> test_addImport_after_library() async {
|
|
await analyze('''
|
|
library foo;
|
|
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:collection/collection.dart', 'IterableExtension')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
library foo;
|
|
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_after_library_before_other() async {
|
|
addPackageFile('fixnum', 'fixnum.dart', '');
|
|
await analyze('''
|
|
library foo;
|
|
|
|
import 'package:fixnum/fixnum.dart';
|
|
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:collection/collection.dart', 'IterableExtension')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
library foo;
|
|
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
import 'package:fixnum/fixnum.dart';
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_atEnd_multiple() async {
|
|
addPackageFile('args', 'args.dart', '');
|
|
await analyze('''
|
|
import 'package:args/args.dart';
|
|
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:fixnum/fixnum.dart', 'Int32')
|
|
..addImport('package:collection/collection.dart', 'IterableExtension')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:args/args.dart';
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
import 'package:fixnum/fixnum.dart' show Int32;
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_atStart_multiple() async {
|
|
addPackageFile('fixnum', 'fixnum.dart', '');
|
|
await analyze('''
|
|
import 'package:fixnum/fixnum.dart';
|
|
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:collection/collection.dart', 'IterableExtension')
|
|
..addImport('package:args/args.dart', 'ArgParser')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:args/args.dart' show ArgParser;
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
import 'package:fixnum/fixnum.dart';
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_before_export() async {
|
|
await analyze('''
|
|
export 'dart:async';
|
|
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:collection/collection.dart', 'IterableExtension')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
export 'dart:async';
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_no_previous_imports_multiple() async {
|
|
await analyze('''
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('dart:async', 'Future')
|
|
..addImport('dart:math', 'sin')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'dart:async' show Future;
|
|
import 'dart:math' show sin;
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_recursive() async {
|
|
addPackageFile('args', 'args.dart', '');
|
|
addPackageFile('fixnum', 'fixnum.dart', 'class Int32 {}');
|
|
await analyze('''
|
|
import 'package:args/args.dart';
|
|
import 'package:fixnum/fixnum.dart' show Int32;
|
|
|
|
main() => null;
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:collection/collection.dart', 'IterableExtension'),
|
|
findNode.import('package:fixnum').combinators[0]:
|
|
NodeChangeForShowCombinator()..addName('Int64'),
|
|
findNode.expression('null'): NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:args/args.dart';
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
import 'package:fixnum/fixnum.dart' show Int32, Int64;
|
|
|
|
main() => null!;
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_sort_shown_names() async {
|
|
await analyze('''
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('dart:async', 'Stream')
|
|
..addImport('dart:async', 'Future')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'dart:async' show Future, Stream;
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_sorted() async {
|
|
addPackageFile('args', 'args.dart', '');
|
|
addPackageFile('fixnum', 'fixnum.dart', '');
|
|
await analyze('''
|
|
import 'package:args/args.dart';
|
|
import 'package:fixnum/fixnum.dart';
|
|
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:collection/collection.dart', 'IterableExtension')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:args/args.dart';
|
|
import 'package:collection/collection.dart' show IterableExtension;
|
|
import 'package:fixnum/fixnum.dart';
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addImport_sorted_multiple() async {
|
|
addPackageFile('collection', 'collection.dart', '');
|
|
await analyze('''
|
|
import 'package:collection/collection.dart';
|
|
|
|
main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..addImport('package:fixnum/fixnum.dart', 'Int32')
|
|
..addImport('package:args/args.dart', 'ArgParser')
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:args/args.dart' show ArgParser;
|
|
import 'package:collection/collection.dart';
|
|
import 'package:fixnum/fixnum.dart' show Int32;
|
|
|
|
main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_addRequired() async {
|
|
await analyze('f({int x}) => 0;');
|
|
var previewInfo = run({
|
|
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
|
|
..addRequiredKeyword = true
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f({required int x}) => 0;');
|
|
}
|
|
|
|
Future<void> test_addRequired_afterMetadata() async {
|
|
await analyze('f({@deprecated int x}) => 0;');
|
|
var previewInfo = run({
|
|
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
|
|
..addRequiredKeyword = true
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f({@deprecated required int x}) => 0;');
|
|
}
|
|
|
|
Future<void> test_addRequired_afterMetadata_andRequiredAnnotation() async {
|
|
addMetaPackage();
|
|
var content = '''
|
|
import 'package:meta/meta.dart';
|
|
f({@required @deprecated int x}) {}
|
|
''';
|
|
await analyze(content);
|
|
var annotation = findNode.annotation('required');
|
|
var previewInfo = run({
|
|
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
|
|
..addRequiredKeyword = true
|
|
..annotationToRemove = annotation
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:meta/meta.dart';
|
|
f({@deprecated required int x}) {}
|
|
''');
|
|
expect(previewInfo.values, hasLength(2));
|
|
|
|
expect(previewInfo[content.indexOf('int ')], hasLength(1));
|
|
expect(previewInfo[content.indexOf('int ')].single.isInsertion, true);
|
|
expect(previewInfo[content.indexOf('@required')], isNotNull);
|
|
expect(previewInfo[content.indexOf('@required')].single.isDeletion, true);
|
|
}
|
|
|
|
Future<void> test_addRequired_afterMetadata_beforeFinal() async {
|
|
await analyze('f({@deprecated final int x}) => 0;');
|
|
var previewInfo = run({
|
|
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
|
|
..addRequiredKeyword = true
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f({@deprecated required final int x}) => 0;');
|
|
}
|
|
|
|
Future<void> test_addRequired_afterMetadata_beforeFunctionTyped() async {
|
|
await analyze('f({@deprecated int x()}) => 0;');
|
|
var previewInfo = run({
|
|
findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter()
|
|
..addRequiredKeyword = true
|
|
});
|
|
expect(
|
|
previewInfo.applyTo(code), 'f({@deprecated required int x()}) => 0;');
|
|
}
|
|
|
|
Future<void> test_addShownName_atEnd_multiple() async {
|
|
await analyze("import 'dart:math' show cos;");
|
|
var previewInfo = run({
|
|
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
|
|
..addName('tan')
|
|
..addName('sin')
|
|
});
|
|
expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
|
|
}
|
|
|
|
Future<void> test_addShownName_atStart_multiple() async {
|
|
await analyze("import 'dart:math' show tan;");
|
|
var previewInfo = run({
|
|
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
|
|
..addName('sin')
|
|
..addName('cos')
|
|
});
|
|
expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
|
|
}
|
|
|
|
Future<void> test_addShownName_sorted() async {
|
|
await analyze("import 'dart:math' show cos, tan;");
|
|
var previewInfo = run({
|
|
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
|
|
..addName('sin')
|
|
});
|
|
expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
|
|
}
|
|
|
|
Future<void> test_addShownName_sorted_multiple() async {
|
|
await analyze("import 'dart:math' show sin;");
|
|
var previewInfo = run({
|
|
findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator()
|
|
..addName('tan')
|
|
..addName('cos')
|
|
});
|
|
expect(previewInfo.applyTo(code), "import 'dart:math' show cos, sin, tan;");
|
|
}
|
|
|
|
Future<void> test_adjacentFixes() async {
|
|
await analyze('f(a, b) => a + b;');
|
|
var aRef = findNode.simple('a +');
|
|
var bRef = findNode.simple('b;');
|
|
var previewInfo = run({
|
|
aRef: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()),
|
|
bRef: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()),
|
|
findNode.binary('a + b'): NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a, b) => (a! + b!)!;');
|
|
}
|
|
|
|
Future<void> test_argument_list_drop_all_arguments() async {
|
|
var content = '''
|
|
f([int x, int y]) => null;
|
|
g(int x, int y) => f(x, y);
|
|
''';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList()
|
|
..dropArgument(findNode.simple('y);'), null)
|
|
..dropArgument(findNode.simple('x, y'), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
f([int x, int y]) => null;
|
|
g(int x, int y) => f();
|
|
''');
|
|
}
|
|
|
|
Future<void> test_argument_list_drop_one_argument() async {
|
|
var content = '''
|
|
f([int x, int y]) => null;
|
|
g(int x, int y) => f(x, y);
|
|
''';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList()
|
|
..dropArgument(findNode.simple('y);'), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
f([int x, int y]) => null;
|
|
g(int x, int y) => f(x);
|
|
''');
|
|
}
|
|
|
|
Future<void> test_argument_list_recursive_changes() async {
|
|
var content = '''
|
|
f([int x, int y]) => null;
|
|
g(int x, int y) => f(x, y);
|
|
''';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList()
|
|
..dropArgument(findNode.simple('y);'), null),
|
|
findNode.simple('x, y'): NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
f([int x, int y]) => null;
|
|
g(int x, int y) => f(x!);
|
|
''');
|
|
}
|
|
|
|
Future<void> test_assignment_add_null_check() async {
|
|
var content = 'f(int x, int y) => x += y;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.assignment('+='): NodeChangeForAssignment()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int x, int y) => (x += y)!;');
|
|
}
|
|
|
|
Future<void> test_assignment_change_lhs() async {
|
|
var content = 'f(List<int> x, int y) => x[0] += y;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.assignment('+='): NodeChangeForAssignment(),
|
|
findNode.index('[0]').target: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(List<int> x, int y) => x![0] += y;');
|
|
}
|
|
|
|
Future<void> test_assignment_change_rhs() async {
|
|
var content = 'f(int x, int y) => x += y;';
|
|
await analyze(content);
|
|
var assignment = findNode.assignment('+=');
|
|
var previewInfo = run({
|
|
assignment: NodeChangeForAssignment(),
|
|
assignment.rightHandSide: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int x, int y) => x += y!;');
|
|
}
|
|
|
|
Future<void> test_assignment_compound_with_bad_combined_type() async {
|
|
var content = 'f(int x, int y) => x += y;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.assignment('+='): NodeChangeForAssignment()
|
|
..hasBadCombinedType = true
|
|
});
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('+=')].single;
|
|
expect(edit.info.description,
|
|
NullabilityFixDescription.compoundAssignmentHasBadCombinedType);
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '+='.length);
|
|
}
|
|
|
|
Future<void> test_assignment_compound_with_nullable_source() async {
|
|
var content = 'f(int x, int y) => x += y;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.assignment('+='): NodeChangeForAssignment()
|
|
..hasNullableSource = true
|
|
});
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('+=')].single;
|
|
expect(edit.info.description,
|
|
NullabilityFixDescription.compoundAssignmentHasNullableSource);
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '+='.length);
|
|
}
|
|
|
|
Future<void> test_assignment_introduce_as() async {
|
|
var content = 'f(int x, int y) => x += y;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.assignment('+='): NodeChangeForAssignment()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
|
|
null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int x, int y) => (x += y) as int;');
|
|
}
|
|
|
|
Future<void> test_assignment_weak_null_aware() async {
|
|
var content = 'f(int x, int y) => x ??= y;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.assignment('??='): NodeChangeForAssignment()
|
|
..isWeakNullAware = true
|
|
}, warnOnWeakCode: true);
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('??=')].single;
|
|
expect(edit.info.description,
|
|
NullabilityFixDescription.nullAwareAssignmentUnnecessaryInStrongMode);
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '??='.length);
|
|
}
|
|
|
|
Future<void> test_assignment_weak_null_aware_remove() async {
|
|
var content = 'f(int x, int y) => x ??= y;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.assignment('??='): NodeChangeForAssignment()
|
|
..isWeakNullAware = true
|
|
}, warnOnWeakCode: false);
|
|
expect(previewInfo.applyTo(code), 'f(int x, int y) => x;');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_changesInKeptCode() async {
|
|
await analyze('''
|
|
f(int i, int/*?*/ j) {
|
|
if (i != null) j.isEven;
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = true,
|
|
findNode.simple('j.isEven'): NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
f(int i, int/*?*/ j) {
|
|
j!.isEven;
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_changesInKeptCode_expandBlock() async {
|
|
await analyze('''
|
|
f(int i, int/*?*/ j) {
|
|
if (i != null) {
|
|
j.isEven;
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = true,
|
|
findNode.simple('j.isEven'): NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
f(int i, int/*?*/ j) {
|
|
j!.isEven;
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_element_delete_drop_completely() async {
|
|
await analyze('''
|
|
List<int> f(int i) {
|
|
return [if (i == null) null];
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
List<int> f(int i) {
|
|
return [];
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_eliminateDeadIf_element_delete_drop_completely_not_in_sequence() async {
|
|
await analyze('''
|
|
List<int> f(int i) {
|
|
return [for (var x in [1, 2, 3]) if (i == null) null];
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false
|
|
});
|
|
// This is a little kludgy; we could drop the `for` loop, but it's difficult
|
|
// to do so, and this is a rare enough corner case that it doesn't seem
|
|
// worth it. Replacing the `if` with `...{}` has the right effect, since
|
|
// it expands to nothing.
|
|
expect(previewInfo.applyTo(code), '''
|
|
List<int> f(int i) {
|
|
return [for (var x in [1, 2, 3]) ...{}];
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_element_delete_keep_else() async {
|
|
await analyze('''
|
|
List<int> f(int i) {
|
|
return [if (i == null) null else i + 1];
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
List<int> f(int i) {
|
|
return [i + 1];
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_element_delete_keep_then() async {
|
|
await analyze('''
|
|
List<int> f(int i) {
|
|
return [if (i == null) null else i + 1];
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
List<int> f(int i) {
|
|
return [null];
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_expression_delete_keep_else() async {
|
|
await analyze('''
|
|
int f(int i) {
|
|
return i == null ? null : i + 1;
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.conditionalExpression('=='): NodeChangeForConditionalExpression()
|
|
..conditionValue = false
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
int f(int i) {
|
|
return i + 1;
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_expression_delete_keep_then() async {
|
|
await analyze('''
|
|
int f(int i) {
|
|
return i == null ? null : i + 1;
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.conditionalExpression('=='): NodeChangeForConditionalExpression()
|
|
..conditionValue = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
int f(int i) {
|
|
return null;
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_statement_comment_keep_else() async {
|
|
await analyze('''
|
|
int f(int i) {
|
|
if (i == null) {
|
|
return null;
|
|
} else {
|
|
return i + 1;
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = false
|
|
}, removeViaComments: true);
|
|
expect(previewInfo.applyTo(code), '''
|
|
int f(int i) {
|
|
/* if (i == null) {
|
|
return null;
|
|
} else {
|
|
*/ return i + 1; /*
|
|
} */
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_statement_comment_keep_then() async {
|
|
await analyze('''
|
|
int f(int i) {
|
|
if (i == null) {
|
|
return null;
|
|
} else {
|
|
return i + 1;
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = true
|
|
}, removeViaComments: true);
|
|
expect(previewInfo.applyTo(code), '''
|
|
int f(int i) {
|
|
/* if (i == null) {
|
|
*/ return null; /*
|
|
} else {
|
|
return i + 1;
|
|
} */
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_eliminateDeadIf_statement_delete_drop_completely_false() async {
|
|
await analyze('''
|
|
void f(int i) {
|
|
if (i == null) {
|
|
print('null');
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = false
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
void f(int i) {}
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_eliminateDeadIf_statement_delete_drop_completely_not_in_block() async {
|
|
await analyze('''
|
|
void f(int i) {
|
|
while (true)
|
|
if (i == null) {
|
|
print('null');
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = false
|
|
});
|
|
// Note: formatting is a little weird here but it's such a rare case that
|
|
// we don't care.
|
|
expect(previewInfo.applyTo(code), '''
|
|
void f(int i) {
|
|
while (true)
|
|
{}
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_eliminateDeadIf_statement_delete_drop_completely_true() async {
|
|
await analyze('''
|
|
void f(int i) {
|
|
if (i != null) {} else {
|
|
print('null');
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
void f(int i) {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_statement_delete_keep_else() async {
|
|
await analyze('''
|
|
int f(int i) {
|
|
if (i == null) {
|
|
return null;
|
|
} else {
|
|
return i + 1;
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = false
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
int f(int i) {
|
|
return i + 1;
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_eliminateDeadIf_statement_delete_keep_then() async {
|
|
await analyze('''
|
|
int f(int i) {
|
|
if (i != null) {
|
|
return i + 1;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
int f(int i) {
|
|
return i + 1;
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_eliminateDeadIf_statement_delete_keep_then_declaration() async {
|
|
await analyze('''
|
|
void f(int i, String callback()) {
|
|
if (i != null) {
|
|
var i = callback();
|
|
} else {
|
|
return;
|
|
}
|
|
print(i);
|
|
}
|
|
''');
|
|
// In this case we have to keep the block so that the scope of `var i`
|
|
// doesn't widen.
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
void f(int i, String callback()) {
|
|
{
|
|
var i = callback();
|
|
}
|
|
print(i);
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_introduceAs_distant_parens_no_longer_needed() async {
|
|
// Note: in principle it would be nice to delete the outer parens, but it's
|
|
// difficult to see that they used to be necessary and aren't anymore, so we
|
|
// leave them.
|
|
await analyze('f(a, c) => a..b = (throw c..d);');
|
|
var cd = findNode.cascade('c..d');
|
|
var previewInfo = run({
|
|
cd: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(
|
|
previewInfo.applyTo(code), 'f(a, c) => a..b = (throw (c..d) as int);');
|
|
}
|
|
|
|
Future<void> test_introduceAs_dynamic() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.dynamicType, isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(Object o) => o as dynamic;');
|
|
}
|
|
|
|
Future<void> test_introduceAs_favorPrefix() async {
|
|
await analyze('''
|
|
import 'dart:async' as a;
|
|
import 'dart:async';
|
|
f(Object o) => o;
|
|
''');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.futureNullType,
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'dart:async' as a;
|
|
import 'dart:async';
|
|
f(Object o) => o as a.Future<Null>;
|
|
''');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [],
|
|
parameters: [],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(Object o) => o as bool Function();');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_formal_bound() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [
|
|
TypeParameterElementImpl.synthetic('T')
|
|
..bound = nnbdTypeProvider.numType
|
|
],
|
|
parameters: [],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f(Object o) => o as bool Function<T extends num>();');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_formal_bound_dynamic() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [
|
|
TypeParameterElementImpl.synthetic('T')
|
|
..bound = nnbdTypeProvider.dynamicType
|
|
],
|
|
parameters: [],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(
|
|
previewInfo.applyTo(code), 'f(Object o) => o as bool Function<T>();');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_formal_bound_object() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [
|
|
TypeParameterElementImpl.synthetic('T')
|
|
..bound = nnbdTypeProvider.objectType
|
|
],
|
|
parameters: [],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f(Object o) => o as bool Function<T extends Object>();');
|
|
}
|
|
|
|
Future<void>
|
|
test_introduceAs_functionType_formal_bound_object_question() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [
|
|
TypeParameterElementImpl.synthetic('T')
|
|
..bound = (nnbdTypeProvider.objectType as TypeImpl)
|
|
.withNullability(NullabilitySuffix.question)
|
|
],
|
|
parameters: [],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(
|
|
previewInfo.applyTo(code), 'f(Object o) => o as bool Function<T>();');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_formal_bound_question() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [
|
|
TypeParameterElementImpl.synthetic('T')
|
|
..bound = (nnbdTypeProvider.numType as TypeImpl)
|
|
.withNullability(NullabilitySuffix.question)
|
|
],
|
|
parameters: [],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f(Object o) => o as bool Function<T extends num?>();');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_formals() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [
|
|
TypeParameterElementImpl.synthetic('T'),
|
|
TypeParameterElementImpl.synthetic('U')
|
|
],
|
|
parameters: [],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f(Object o) => o as bool Function<T, U>();');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_parameters() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [],
|
|
parameters: [
|
|
ParameterElementImpl.synthetic('x',
|
|
nnbdTypeProvider.intType, ParameterKind.REQUIRED),
|
|
ParameterElementImpl.synthetic(
|
|
'y', nnbdTypeProvider.numType, ParameterKind.REQUIRED)
|
|
],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f(Object o) => o as bool Function(int, num);');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_parameters_named() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [],
|
|
parameters: [
|
|
ParameterElementImpl.synthetic(
|
|
'x', nnbdTypeProvider.intType, ParameterKind.NAMED),
|
|
ParameterElementImpl.synthetic(
|
|
'y', nnbdTypeProvider.numType, ParameterKind.NAMED)
|
|
],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f(Object o) => o as bool Function({int x, num y});');
|
|
}
|
|
|
|
Future<void> test_introduceAs_functionType_parameters_optional() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
FunctionTypeImpl(
|
|
returnType: nnbdTypeProvider.boolType,
|
|
typeFormals: [],
|
|
parameters: [
|
|
ParameterElementImpl.synthetic('x',
|
|
nnbdTypeProvider.intType, ParameterKind.POSITIONAL),
|
|
ParameterElementImpl.synthetic('y',
|
|
nnbdTypeProvider.numType, ParameterKind.POSITIONAL)
|
|
],
|
|
nullabilitySuffix: NullabilitySuffix.none),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code),
|
|
'f(Object o) => o as bool Function([int, num]);');
|
|
}
|
|
|
|
Future<void> test_introduceAs_interfaceType_parameterized() async {
|
|
await analyze('f(Object o) => o;');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(
|
|
nnbdTypeProvider.mapType(
|
|
nnbdTypeProvider.intType, nnbdTypeProvider.boolType),
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(Object o) => o as Map<int, bool>;');
|
|
}
|
|
|
|
Future<void> test_introduceAs_no_parens() async {
|
|
await analyze('f(a, b) => a | b;');
|
|
var expr = findNode.binary('a | b');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a, b) => a | b as int;');
|
|
}
|
|
|
|
Future<void> test_introduceAs_parens() async {
|
|
await analyze('f(a, b) => a < b;');
|
|
var expr = findNode.binary('a < b');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.boolType, isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a, b) => (a < b) as bool;');
|
|
}
|
|
|
|
Future<void> test_introduceAs_usePrefix() async {
|
|
await analyze('''
|
|
import 'dart:async' as a;
|
|
f(Object o) => o;
|
|
''');
|
|
var expr = findNode.simple('o;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.futureNullType,
|
|
isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'dart:async' as a;
|
|
f(Object o) => o as a.Future<Null>;
|
|
''');
|
|
}
|
|
|
|
Future<void> test_introduceAs_withNullCheck() async {
|
|
await analyze('f(x) => x;');
|
|
var expr = findNode.simple('x;');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
|
|
_MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(x) => x! as int;');
|
|
}
|
|
|
|
Future<void> test_keep_redundant_parens() async {
|
|
await analyze('f(a, b, c) => a + (b * c);');
|
|
var previewInfo = run({});
|
|
expect(previewInfo, isEmpty);
|
|
}
|
|
|
|
Future<void> test_makeNullable() async {
|
|
await analyze('f(int x) {}');
|
|
var typeName = findNode.typeName('int');
|
|
var previewInfo = run({
|
|
typeName: NodeChangeForTypeAnnotation()
|
|
..recordNullability(
|
|
MockDecoratedType(
|
|
MockDartType(toStringValueWithoutNullability: 'int')),
|
|
true)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int? x) {}');
|
|
}
|
|
|
|
Future<void> test_methodName_change() async {
|
|
await analyze('f() => f();');
|
|
var previewInfo = run({
|
|
findNode.methodInvocation('f();').methodName: NodeChangeForMethodName()
|
|
..replaceWith('g', null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f() => g();');
|
|
}
|
|
|
|
Future<void> test_methodName_no_change() async {
|
|
await analyze('f() => f();');
|
|
var previewInfo = run({
|
|
findNode.methodInvocation('f();').methodName: NodeChangeForMethodName()
|
|
});
|
|
expect(previewInfo, isNull);
|
|
}
|
|
|
|
Future<void> test_noChangeToTypeAnnotation() async {
|
|
await analyze('int x = 0;');
|
|
var typeName = findNode.typeName('int');
|
|
var previewInfo = run({
|
|
typeName: NodeChangeForTypeAnnotation()
|
|
..recordNullability(
|
|
MockDecoratedType(
|
|
MockDartType(toStringValueWithoutNullability: 'int')),
|
|
false)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'int x = 0;');
|
|
expect(previewInfo.applyTo(code, includeInformative: true), 'int x = 0;');
|
|
expect(previewInfo.values.single.single.info.description.appliedMessage,
|
|
"Type 'int' was not made nullable");
|
|
}
|
|
|
|
Future<void> test_noInfoForTypeAnnotation() async {
|
|
await analyze('int x = 0;');
|
|
var typeName = findNode.typeName('int');
|
|
var previewInfo = run({typeName: NodeChangeForTypeAnnotation()});
|
|
expect(previewInfo, null);
|
|
}
|
|
|
|
Future<void> test_noValidMigration() async {
|
|
await analyze('f(a) => null;');
|
|
var literal = findNode.nullLiteral('null');
|
|
var previewInfo = run({
|
|
literal: NodeChangeForExpression()
|
|
..addExpressionChange(
|
|
NoValidMigrationChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), code);
|
|
expect(previewInfo.applyTo(code, includeInformative: true),
|
|
'f(a) => null /* no valid migration */;');
|
|
}
|
|
|
|
Future<void> test_nullCheck_index_cascadeResult() async {
|
|
await analyze('f(a) => a..[0].c;');
|
|
var index = findNode.index('[0]');
|
|
var previewInfo = run({
|
|
index: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a) => a..[0]!.c;');
|
|
}
|
|
|
|
Future<void> test_nullCheck_methodInvocation_cascadeResult() async {
|
|
await analyze('f(a) => a..b().c;');
|
|
var method = findNode.methodInvocation('b()');
|
|
var previewInfo = run({
|
|
method: NodeChangeForMethodInvocation()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a) => a..b()!.c;');
|
|
}
|
|
|
|
Future<void> test_nullCheck_no_parens() async {
|
|
await analyze('f(a) => a++;');
|
|
var expr = findNode.postfix('a++');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a) => a++!;');
|
|
}
|
|
|
|
Future<void> test_nullCheck_parens() async {
|
|
await analyze('f(a) => -a;');
|
|
var expr = findNode.prefix('-a');
|
|
var previewInfo = run({
|
|
expr: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a) => (-a)!;');
|
|
}
|
|
|
|
Future<void> test_nullCheck_propertyAccess_cascadeResult() async {
|
|
await analyze('f(a) => a..b.c;');
|
|
var property = findNode.propertyAccess('b');
|
|
var previewInfo = run({
|
|
property: NodeChangeForPropertyAccess()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(a) => a..b!.c;');
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_annotated() async {
|
|
await analyze('f({@deprecated x = 0}) {}');
|
|
var previewInfo = run({
|
|
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f({@deprecated int x = 0}) {}');
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_declared_with_covariant() async {
|
|
await analyze('''
|
|
class C {
|
|
m({num x}) {}
|
|
}
|
|
class D extends C {
|
|
m({covariant x = 3}) {}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.simpleParameter('x = 3'): NodeChangeForSimpleFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
class C {
|
|
m({num x}) {}
|
|
}
|
|
class D extends C {
|
|
m({covariant int x = 3}) {}
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_declared_with_final() async {
|
|
await analyze('f({final x = 0}) {}');
|
|
var previewInfo = run({
|
|
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f({final int x = 0}) {}');
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_declared_with_var() async {
|
|
await analyze('f({var x = 0}) {}');
|
|
var previewInfo = run({
|
|
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f({int x = 0}) {}');
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_named() async {
|
|
await analyze('f({x = 0}) {}');
|
|
var previewInfo = run({
|
|
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f({int x = 0}) {}');
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_no() async {
|
|
await analyze('f([x = 0]) {}');
|
|
var previewInfo = run(
|
|
{findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()});
|
|
expect(previewInfo, isNull);
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_optional_insert() async {
|
|
await analyze('f([x = 0]) {}');
|
|
var previewInfo = run({
|
|
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f([int x = 0]) {}');
|
|
}
|
|
|
|
Future<void> test_parameter_addExplicitType_prefixed_type() async {
|
|
await analyze('''
|
|
import 'dart:core' as core;
|
|
f({x = 0}) {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'dart:core' as core;
|
|
f({core.int x = 0}) {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_parameter_field_formal_addExplicitType() async {
|
|
await analyze('''
|
|
class C {
|
|
int x;
|
|
C(this.x) {}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.fieldFormalParameter('this.x'):
|
|
NodeChangeForFieldFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
class C {
|
|
int x;
|
|
C(int this.x) {}
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_parameter_field_formal_addExplicitType_declared_with_final() async {
|
|
await analyze('''
|
|
class C {
|
|
int x;
|
|
C(final this.x) {}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.fieldFormalParameter('this.x'):
|
|
NodeChangeForFieldFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
class C {
|
|
int x;
|
|
C(final int this.x) {}
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_parameter_field_formal_addExplicitType_declared_with_var() async {
|
|
await analyze('''
|
|
class C {
|
|
int x;
|
|
C(var this.x) {}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.fieldFormalParameter('this.x'):
|
|
NodeChangeForFieldFormalParameter()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
class C {
|
|
int x;
|
|
C(int this.x) {}
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_parameter_field_formal_addExplicitType_no() async {
|
|
await analyze('''
|
|
class C {
|
|
int x;
|
|
C(this.x) {}
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.fieldFormalParameter('this.x'):
|
|
NodeChangeForFieldFormalParameter()
|
|
});
|
|
expect(previewInfo, isNull);
|
|
}
|
|
|
|
Future<void> test_post_increment_add_null_check() async {
|
|
var content = 'f(int x) => x++;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.postfix('++'): NodeChangeForPostfixExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int x) => x++!;');
|
|
}
|
|
|
|
Future<void> test_post_increment_change_target() async {
|
|
var content = 'f(List<int> x) => x[0]++;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.postfix('++'): NodeChangeForPostfixExpression(),
|
|
findNode.index('[0]').target: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(List<int> x) => x![0]++;');
|
|
}
|
|
|
|
Future<void> test_post_increment_introduce_as() async {
|
|
var content = 'f(int x) => x++;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.postfix('++'): NodeChangeForPostfixExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
|
|
null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int x) => x++ as int;');
|
|
}
|
|
|
|
Future<void> test_post_increment_with_bad_combined_type() async {
|
|
var content = 'f(int x) => x++;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.postfix('++'): NodeChangeForPostfixExpression()
|
|
..hasBadCombinedType = true
|
|
});
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('++')].single;
|
|
expect(edit.info.description,
|
|
NullabilityFixDescription.compoundAssignmentHasBadCombinedType);
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '++'.length);
|
|
}
|
|
|
|
Future<void> test_post_increment_with_nullable_source() async {
|
|
var content = 'f(int x) => x++;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.postfix('++'): NodeChangeForPostfixExpression()
|
|
..hasNullableSource = true
|
|
});
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('++')].single;
|
|
expect(edit.info.description,
|
|
NullabilityFixDescription.compoundAssignmentHasNullableSource);
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '++'.length);
|
|
}
|
|
|
|
Future<void> test_pre_increment_add_null_check() async {
|
|
var content = 'f(int x) => ++x;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.prefix('++'): NodeChangeForPrefixExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int x) => (++x)!;');
|
|
}
|
|
|
|
Future<void> test_pre_increment_change_target() async {
|
|
var content = 'f(List<int> x) => ++x[0];';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.prefix('++'): NodeChangeForPrefixExpression(),
|
|
findNode.index('[0]').target: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(List<int> x) => ++x![0];');
|
|
}
|
|
|
|
Future<void> test_pre_increment_introduce_as() async {
|
|
var content = 'f(int x) => ++x;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.prefix('++'): NodeChangeForPrefixExpression()
|
|
..addExpressionChange(
|
|
IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false),
|
|
null)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(int x) => ++x as int;');
|
|
}
|
|
|
|
Future<void> test_pre_increment_with_bad_combined_type() async {
|
|
var content = 'f(int x) => ++x;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.prefix('++'): NodeChangeForPrefixExpression()
|
|
..hasBadCombinedType = true
|
|
});
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('++')].single;
|
|
expect(edit.info.description,
|
|
NullabilityFixDescription.compoundAssignmentHasBadCombinedType);
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '++'.length);
|
|
}
|
|
|
|
Future<void> test_pre_increment_with_nullable_source() async {
|
|
var content = 'f(int x) => ++x;';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.prefix('++'): NodeChangeForPrefixExpression()
|
|
..hasNullableSource = true
|
|
});
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('++')].single;
|
|
expect(edit.info.description,
|
|
NullabilityFixDescription.compoundAssignmentHasNullableSource);
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '++'.length);
|
|
}
|
|
|
|
Future<void>
|
|
test_removeAs_in_cascade_target_no_parens_needed_cascade() async {
|
|
await analyze('f(a) => ((a..b) as dynamic)..c;');
|
|
var cascade = findNode.cascade('a..b');
|
|
var cast = cascade.parent.parent;
|
|
var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a) => a..b..c;');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeAs_in_cascade_target_no_parens_needed_conditional() async {
|
|
// TODO(paulberry): would it be better to keep the parens in this case for
|
|
// clarity, even though they're not needed?
|
|
await analyze('f(a, b, c) => ((a ? b : c) as dynamic)..d;');
|
|
var conditional = findNode.conditionalExpression('a ? b : c');
|
|
var cast = conditional.parent.parent;
|
|
var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b, c) => a ? b : c..d;');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeAs_in_cascade_target_parens_needed_assignment() async {
|
|
await analyze('f(a, b) => ((a = b) as dynamic)..c;');
|
|
var assignment = findNode.assignment('a = b');
|
|
var cast = assignment.parent.parent;
|
|
var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b) => (a = b)..c;');
|
|
}
|
|
|
|
Future<void> test_removeAs_in_cascade_target_parens_needed_throw() async {
|
|
await analyze('f(a) => ((throw a) as dynamic)..b;');
|
|
var throw_ = findNode.throw_('throw a');
|
|
var cast = throw_.parent.parent;
|
|
var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a) => (throw a)..b;');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeAs_lower_precedence_do_not_remove_inner_parens() async {
|
|
await analyze('f(a, b, c) => (a == b) as Null == c;');
|
|
var expr = findNode.binary('a == b');
|
|
var previewInfo =
|
|
run({expr.parent.parent: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b, c) => (a == b) == c;');
|
|
}
|
|
|
|
Future<void> test_removeAs_lower_precedence_remove_inner_parens() async {
|
|
await analyze('f(a, b) => (a == b) as Null;');
|
|
var expr = findNode.binary('a == b');
|
|
var previewInfo =
|
|
run({expr.parent.parent: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b) => a == b;');
|
|
}
|
|
|
|
Future<void> test_removeAs_parens_needed_due_to_cascade() async {
|
|
// Note: parens are needed, and they could either be around `c..d` or around
|
|
// `throw c..d`. In an ideal world, we would see that we can just keep the
|
|
// parens we have, but this is difficult because we don't see that the
|
|
// parens are needed until we walk far enough up the AST to see that we're
|
|
// inside a casade expression. So we drop the parens and then create new
|
|
// ones surrounding `throw c..d`.
|
|
//
|
|
// Strictly speaking the code we produce is correct, it's just making a
|
|
// slightly larger edit than necessary. This is presumably a really rare
|
|
// corner case so for now we're not worrying about it.
|
|
await analyze('f(a, c) => a..b = throw (c..d) as int;');
|
|
var cd = findNode.cascade('c..d');
|
|
var cast = cd.parent.parent;
|
|
var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, c) => a..b = (throw c..d);');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeAs_parens_needed_due_to_cascade_in_conditional_else() async {
|
|
await analyze('f(a, b, c) => a ? b : (c..d) as int;');
|
|
var cd = findNode.cascade('c..d');
|
|
var cast = cd.parent.parent;
|
|
var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b, c) => a ? b : (c..d);');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeAs_parens_needed_due_to_cascade_in_conditional_then() async {
|
|
await analyze('f(a, b, d) => a ? (b..c) as int : d;');
|
|
var bc = findNode.cascade('b..c');
|
|
var cast = bc.parent.parent;
|
|
var previewInfo = run({cast: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b, d) => a ? (b..c) : d;');
|
|
}
|
|
|
|
Future<void> test_removeAs_raise_precedence_do_not_remove_parens() async {
|
|
await analyze('f(a, b, c) => a | (b | c as int);');
|
|
var expr = findNode.binary('b | c');
|
|
var previewInfo =
|
|
run({expr.parent: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b, c) => a | (b | c);');
|
|
}
|
|
|
|
Future<void> test_removeAs_raise_precedence_no_parens_to_remove() async {
|
|
await analyze('f(a, b, c) => a = b | c as int;');
|
|
var expr = findNode.binary('b | c');
|
|
var previewInfo =
|
|
run({expr.parent: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b, c) => a = b | c;');
|
|
}
|
|
|
|
Future<void> test_removeAs_raise_precedence_remove_parens() async {
|
|
await analyze('f(a, b, c) => a < (b | c as int);');
|
|
var expr = findNode.binary('b | c');
|
|
var previewInfo =
|
|
run({expr.parent: NodeChangeForAsExpression()..removeAs = true});
|
|
expect(previewInfo.applyTo(code), 'f(a, b, c) => a < b | c;');
|
|
}
|
|
|
|
Future<void> test_removeLanguageVersion() async {
|
|
await analyze('''
|
|
//@dart=2.6
|
|
void main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..removeLanguageVersionComment = true
|
|
});
|
|
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
|
|
expect(previewInfo.applyTo(code), '\nvoid main() {}\n');
|
|
}
|
|
|
|
Future<void> test_removeLanguageVersion_after_license() async {
|
|
await analyze('''
|
|
// Some licensing stuff here...
|
|
// Some copyrighting stuff too...
|
|
// etc...
|
|
// @dart = 2.6
|
|
void main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..removeLanguageVersionComment = true
|
|
});
|
|
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
|
|
expect(previewInfo.applyTo(code), '''
|
|
// Some licensing stuff here...
|
|
// Some copyrighting stuff too...
|
|
// etc...
|
|
|
|
void main() {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_removeLanguageVersion_spaces() async {
|
|
await analyze('''
|
|
// @dart = 2.6
|
|
void main() {}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..removeLanguageVersionComment = true
|
|
});
|
|
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
|
|
expect(previewInfo.applyTo(code), '\nvoid main() {}\n');
|
|
}
|
|
|
|
Future<void> test_removeLanguageVersion_withOtherChanges() async {
|
|
await analyze('''
|
|
//@dart=2.6
|
|
int f() => null;
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.unit: NodeChangeForCompilationUnit()
|
|
..removeLanguageVersionComment = true,
|
|
findNode.typeAnnotation('int'): NodeChangeForTypeAnnotation()
|
|
..recordNullability(
|
|
MockDecoratedType(
|
|
MockDartType(toStringValueWithoutNullability: 'int')),
|
|
true)
|
|
});
|
|
// TODO(mfairhurst): Remove beginning \n once it renders properly in preview
|
|
expect(previewInfo.applyTo(code), '\nint? f() => null;\n');
|
|
}
|
|
|
|
Future<void> test_removeNullAwarenessFromMethodInvocation() async {
|
|
await analyze('f(x) => x?.m();');
|
|
var methodInvocation = findNode.methodInvocation('?.');
|
|
var previewInfo = run({
|
|
methodInvocation: NodeChangeForMethodInvocation()
|
|
..removeNullAwareness = true
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(x) => x.m();');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeNullAwarenessFromMethodInvocation_changeArgument() async {
|
|
await analyze('f(x) => x?.m(x);');
|
|
var methodInvocation = findNode.methodInvocation('?.');
|
|
var argument = findNode.simple('x);');
|
|
var previewInfo = run({
|
|
methodInvocation: NodeChangeForMethodInvocation()
|
|
..removeNullAwareness = true,
|
|
argument: NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(x) => x.m(x!);');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeNullAwarenessFromMethodInvocation_changeTarget() async {
|
|
await analyze('f(x) => (x as dynamic)?.m();');
|
|
var methodInvocation = findNode.methodInvocation('?.');
|
|
var cast = findNode.as_('as');
|
|
var previewInfo = run({
|
|
methodInvocation: NodeChangeForMethodInvocation()
|
|
..removeNullAwareness = true,
|
|
cast: NodeChangeForAsExpression()..removeAs = true
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(x) => x.m();');
|
|
}
|
|
|
|
Future<void>
|
|
test_removeNullAwarenessFromMethodInvocation_changeTypeArgument() async {
|
|
await analyze('f(x) => x?.m<int>();');
|
|
var methodInvocation = findNode.methodInvocation('?.');
|
|
var typeAnnotation = findNode.typeAnnotation('int');
|
|
var previewInfo = run({
|
|
methodInvocation: NodeChangeForMethodInvocation()
|
|
..removeNullAwareness = true,
|
|
typeAnnotation: NodeChangeForTypeAnnotation()
|
|
..recordNullability(
|
|
MockDecoratedType(
|
|
MockDartType(toStringValueWithoutNullability: 'int')),
|
|
true)
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(x) => x.m<int?>();');
|
|
}
|
|
|
|
Future<void> test_removeNullAwarenessFromPropertyAccess() async {
|
|
await analyze('f(x) => x?.y;');
|
|
var propertyAccess = findNode.propertyAccess('?.');
|
|
var previewInfo = run({
|
|
propertyAccess: NodeChangeForPropertyAccess()..removeNullAwareness = true
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(x) => x.y;');
|
|
}
|
|
|
|
Future<void> test_removeNullAwarenessFromPropertyAccess_changeTarget() async {
|
|
await analyze('f(x) => (x as dynamic)?.y;');
|
|
var propertyAccess = findNode.propertyAccess('?.');
|
|
var cast = findNode.as_('as');
|
|
var previewInfo = run({
|
|
propertyAccess: NodeChangeForPropertyAccess()..removeNullAwareness = true,
|
|
cast: NodeChangeForAsExpression()..removeAs = true
|
|
});
|
|
expect(previewInfo.applyTo(code), 'f(x) => x.y;');
|
|
}
|
|
|
|
Future<void>
|
|
test_requiredAnnotationToRequiredKeyword_leadingAnnotations() async {
|
|
addMetaPackage();
|
|
await analyze('''
|
|
import 'package:meta/meta.dart';
|
|
f({@deprecated @required int x}) {}
|
|
''');
|
|
var annotation = findNode.annotation('required');
|
|
var previewInfo = run({
|
|
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:meta/meta.dart';
|
|
f({@deprecated required int x}) {}
|
|
''');
|
|
expect(previewInfo.values.single.single.isDeletion, true);
|
|
}
|
|
|
|
Future<void> test_requiredAnnotationToRequiredKeyword_prefixed() async {
|
|
addMetaPackage();
|
|
await analyze('''
|
|
import 'package:meta/meta.dart' as meta;
|
|
f({@meta.required int x}) {}
|
|
''');
|
|
var annotation = findNode.annotation('required');
|
|
var previewInfo = run({
|
|
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:meta/meta.dart' as meta;
|
|
f({required int x}) {}
|
|
''');
|
|
expect(previewInfo.values.single.single.isDeletion, true);
|
|
}
|
|
|
|
Future<void> test_requiredAnnotationToRequiredKeyword_renamed() async {
|
|
addMetaPackage();
|
|
await analyze('''
|
|
import 'package:meta/meta.dart';
|
|
const foo = required;
|
|
f({@foo int x}) {}
|
|
''');
|
|
var annotation = findNode.annotation('@foo');
|
|
var previewInfo = run({
|
|
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:meta/meta.dart';
|
|
const foo = required;
|
|
f({required int x}) {}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_requiredAnnotationToRequiredKeyword_simple() async {
|
|
addMetaPackage();
|
|
await analyze('''
|
|
import 'package:meta/meta.dart';
|
|
f({@required int x}) {}
|
|
''');
|
|
var annotation = findNode.annotation('required');
|
|
var previewInfo = run({
|
|
annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:meta/meta.dart';
|
|
f({required int x}) {}
|
|
''');
|
|
expect(previewInfo.values.single.single.isDeletion, true);
|
|
}
|
|
|
|
Future<void>
|
|
test_requiredAnnotationToRequiredKeyword_simple_removeViaComment() async {
|
|
addMetaPackage();
|
|
await analyze('''
|
|
import 'package:meta/meta.dart';
|
|
f({@required int x}) {}
|
|
''');
|
|
var annotation = findNode.annotation('required');
|
|
var previewInfo = run(
|
|
{annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true},
|
|
removeViaComments: true);
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'package:meta/meta.dart';
|
|
f({required int x}) {}
|
|
''');
|
|
expect(previewInfo.values.single.single.isDeletion, true);
|
|
}
|
|
|
|
Future<void> test_variableDeclarationList_addExplicitType_insert() async {
|
|
await analyze('final x = 0;');
|
|
var previewInfo = run({
|
|
findNode.variableDeclarationList('final'):
|
|
NodeChangeForVariableDeclarationList()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), 'final int x = 0;');
|
|
}
|
|
|
|
Future<void> test_variableDeclarationList_addExplicitType_metadata() async {
|
|
await analyze('@deprecated var x = 0;');
|
|
var previewInfo = run({
|
|
findNode.variableDeclarationList('var'):
|
|
NodeChangeForVariableDeclarationList()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), '@deprecated int x = 0;');
|
|
}
|
|
|
|
Future<void> test_variableDeclarationList_addExplicitType_no() async {
|
|
await analyze('var x = 0;');
|
|
var previewInfo = run({
|
|
findNode.variableDeclarationList('var'):
|
|
NodeChangeForVariableDeclarationList()
|
|
});
|
|
expect(previewInfo, isNull);
|
|
}
|
|
|
|
Future<void> test_variableDeclarationList_addExplicitType_otherPlans() async {
|
|
await analyze('var x = 0;');
|
|
var previewInfo = run({
|
|
findNode.variableDeclarationList('var'):
|
|
NodeChangeForVariableDeclarationList()
|
|
..addExplicitType = nnbdTypeProvider.intType,
|
|
findNode.integerLiteral('0'): NodeChangeForExpression()
|
|
..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo())
|
|
});
|
|
expect(previewInfo.applyTo(code), 'int x = 0!;');
|
|
}
|
|
|
|
Future<void> test_variableDeclarationList_addExplicitType_prefixed() async {
|
|
await analyze('''
|
|
import 'dart:core' as core;
|
|
final x = 0;
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.variableDeclarationList('final'):
|
|
NodeChangeForVariableDeclarationList()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), '''
|
|
import 'dart:core' as core;
|
|
final core.int x = 0;
|
|
''');
|
|
}
|
|
|
|
Future<void> test_variableDeclarationList_addExplicitType_replaceVar() async {
|
|
await analyze('var x = 0;');
|
|
var previewInfo = run({
|
|
findNode.variableDeclarationList('var'):
|
|
NodeChangeForVariableDeclarationList()
|
|
..addExplicitType = nnbdTypeProvider.intType
|
|
});
|
|
expect(previewInfo.applyTo(code), 'int x = 0;');
|
|
}
|
|
|
|
Future<void> test_warnOnDeadIf_false() async {
|
|
await analyze('''
|
|
f(int i) {
|
|
if (i == null) print(i);
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = false
|
|
}, warnOnWeakCode: true);
|
|
expect(previewInfo.applyTo(code, includeInformative: true), '''
|
|
f(int i) {
|
|
if (i == null /* == false */) print(i);
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_warnOnDeadIf_true() async {
|
|
await analyze('''
|
|
f(int i) {
|
|
if (i != null) print(i);
|
|
}
|
|
''');
|
|
var previewInfo = run({
|
|
findNode.statement('if'): NodeChangeForIfStatement()
|
|
..conditionValue = true
|
|
}, warnOnWeakCode: true);
|
|
expect(previewInfo.applyTo(code, includeInformative: true), '''
|
|
f(int i) {
|
|
if (i != null /* == true */) print(i);
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<void> test_warnOnNullAwareAccess() async {
|
|
var content = '''
|
|
f(int i) {
|
|
print(i?.isEven);
|
|
}
|
|
''';
|
|
await analyze(content);
|
|
var previewInfo = run({
|
|
findNode.propertyAccess('?.'): NodeChangeForPropertyAccess()
|
|
..removeNullAwareness = true
|
|
}, warnOnWeakCode: true);
|
|
expect(previewInfo.applyTo(code), content);
|
|
expect(previewInfo, hasLength(1));
|
|
var edit = previewInfo[content.indexOf('?')].single;
|
|
expect(edit.isInformative, isTrue);
|
|
expect(edit.length, '?'.length);
|
|
}
|
|
}
|
|
|
|
class FixAggregatorTestBase extends AbstractSingleUnitTest {
|
|
String code;
|
|
|
|
Future<void> analyze(String code) async {
|
|
this.code = code;
|
|
await resolveTestUnit(code);
|
|
}
|
|
|
|
Map<int, List<AtomicEdit>> run(Map<AstNode, NodeChange> changes,
|
|
{bool removeViaComments = false, bool warnOnWeakCode = false}) {
|
|
return FixAggregator.run(testUnit, testCode, changes,
|
|
removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode);
|
|
}
|
|
}
|
|
|
|
class MockDartType implements TypeImpl {
|
|
final String toStringValueWithNullability;
|
|
|
|
final String toStringValueWithoutNullability;
|
|
|
|
const MockDartType(
|
|
{this.toStringValueWithNullability,
|
|
this.toStringValueWithoutNullability});
|
|
|
|
@override
|
|
String getDisplayString({
|
|
bool skipAllDynamicArguments = false,
|
|
bool withNullability = false,
|
|
}) {
|
|
var result = withNullability
|
|
? toStringValueWithNullability
|
|
: toStringValueWithoutNullability;
|
|
expect(result, isNotNull);
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
noSuchMethod(Invocation invocation) {
|
|
return super.noSuchMethod(invocation);
|
|
}
|
|
}
|
|
|
|
class MockDecoratedType implements DecoratedType {
|
|
@override
|
|
final DartType type;
|
|
|
|
const MockDecoratedType(this.type);
|
|
|
|
@override
|
|
NullabilityNode get node =>
|
|
NullabilityNode.forTypeAnnotation(NullabilityNodeTarget.text('test'));
|
|
|
|
@override
|
|
noSuchMethod(Invocation invocation) {
|
|
return super.noSuchMethod(invocation);
|
|
}
|
|
}
|
|
|
|
class _MockInfo implements AtomicEditInfo {
|
|
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
|
}
|