| // 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); |
| } |