| // Copyright (c) 2014, 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:analysis_server/src/services/refactoring/extract_method.dart'; |
| import 'package:analyzer_plugin/protocol/protocol_common.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import 'abstract_refactoring.dart'; |
| |
| void main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(ExtractMethodTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class ExtractMethodTest extends RefactoringTest { |
| @override |
| late ExtractMethodRefactoringImpl refactoring; |
| |
| Future<void> test_bad_assignmentLeftHandSide() async { |
| await indexTestUnit(''' |
| void f() { |
| int aaa; |
| aaa = 0; |
| } |
| '''); |
| _createRefactoringForString('aaa '); |
| return _assertConditionsFatal( |
| 'Cannot extract the left-hand side of an assignment.'); |
| } |
| |
| Future<void> test_bad_comment_selectionEndsInside() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| print(0); |
| /* |
| // end |
| */ |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal('Selection ends inside a comment.'); |
| } |
| |
| Future<void> test_bad_comment_selectionStartsInside() async { |
| await indexTestUnit(''' |
| void f() { |
| /* |
| // start |
| */ |
| print(0); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal('Selection begins inside a comment.'); |
| } |
| |
| Future<void> test_bad_conflict_method_alreadyDeclaresMethod() async { |
| await indexTestUnit(''' |
| class A { |
| void res() {} |
| void f() { |
| // start |
| print(0); |
| // end |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsError( |
| "Class 'A' already declares method with name 'res'."); |
| } |
| |
| Future<void> test_bad_conflict_method_shadowsSuperDeclaration() async { |
| await indexTestUnit(''' |
| class A { |
| void res() {} // marker |
| } |
| class B extends A { |
| void f() { |
| res(); |
| // start |
| print(0); |
| // end |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsError("Created method will shadow method 'A.res'."); |
| } |
| |
| Future<void> test_bad_conflict_topLevel_alreadyDeclaresFunction() async { |
| await indexTestUnit(''' |
| library my.lib; |
| |
| void res() {} |
| void f() { |
| // start |
| print(0); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsError( |
| "Library already declares function with name 'res'."); |
| } |
| |
| Future<void> test_bad_conflict_topLevel_willHideInheritedMemberUsage() async { |
| await indexTestUnit(''' |
| class A { |
| void res() {} |
| } |
| class B extends A { |
| foo() { |
| res(); // marker |
| } |
| } |
| void f() { |
| // start |
| print(0); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsError( |
| "Created function will shadow method 'A.res'."); |
| } |
| |
| Future<void> test_bad_constructor_initializer() async { |
| await indexTestUnit(''' |
| class A { |
| int f; |
| A() : f = 0 {} |
| } |
| '''); |
| _createRefactoringForString('f = 0'); |
| return _assertConditionsFatal( |
| 'Cannot extract a constructor initializer. Select expression part of initializer.'); |
| } |
| |
| Future<void> test_bad_constructor_redirectingConstructor() async { |
| await indexTestUnit(''' |
| class A { |
| A() : this.named(); |
| A.named() {} |
| } |
| '''); |
| _createRefactoringForString('this.named()'); |
| return _assertConditionsFatal( |
| 'Cannot extract a constructor initializer. Select expression part of initializer.'); |
| } |
| |
| Future<void> test_bad_constructor_superConstructor() async { |
| await indexTestUnit(''' |
| class A {} |
| class B extends A { |
| B() : super(); |
| } |
| '''); |
| _createRefactoringForString('super()'); |
| return _assertConditionsFatal( |
| 'Cannot extract a constructor initializer. Select expression part of initializer.'); |
| } |
| |
| Future<void> test_bad_doWhile_body() async { |
| await indexTestUnit(''' |
| void f() { |
| do |
| // start |
| { |
| } |
| // end |
| while (true); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| "Operation not applicable to a 'do' statement's body and expression."); |
| } |
| |
| Future<void> test_bad_emptySelection() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| // end |
| print(0); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Can only extract a single expression or a set of statements.'); |
| } |
| |
| Future<void> test_bad_forLoop_conditionAndUpdaters() async { |
| await indexTestUnit(''' |
| void f() { |
| for ( |
| int i = 0; |
| // start |
| i < 10; |
| i++ |
| // end |
| ) {} |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| "Operation not applicable to a 'for' statement's condition and updaters."); |
| } |
| |
| Future<void> test_bad_forLoop_init() async { |
| await indexTestUnit(''' |
| void f() { |
| for ( |
| // start |
| int i = 0 |
| // end |
| ; i < 10; |
| i++ |
| ) {} |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| "Cannot extract initialization part of a 'for' statement."); |
| } |
| |
| Future<void> test_bad_forLoop_initAndCondition() async { |
| await indexTestUnit(''' |
| void f() { |
| for ( |
| // start |
| int i = 0; |
| i < 10; |
| // end |
| i++ |
| ) {} |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| "Operation not applicable to a 'for' statement's initializer and condition."); |
| } |
| |
| Future<void> test_bad_forLoop_updaters() async { |
| await indexTestUnit(''' |
| void f() { |
| for ( |
| int i = 0; |
| i < 10; |
| // start |
| i++ |
| // end |
| ) {} |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| "Cannot extract increment part of a 'for' statement."); |
| } |
| |
| Future<void> test_bad_forLoop_updatersAndBody() async { |
| await indexTestUnit(''' |
| void f() { |
| for ( |
| int i = 0; |
| i < 10; |
| // start |
| i++ |
| ) {} |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Not all selected statements are enclosed by the same parent statement.'); |
| } |
| |
| Future<void> test_bad_methodName_reference() async { |
| await indexTestUnit(''' |
| void f() { |
| f(); |
| } |
| '''); |
| _createRefactoringWithSuffix('f', '();'); |
| return _assertConditionsFatal('Cannot extract a single method name.'); |
| } |
| |
| Future<void> test_bad_namePartOfDeclaration_function() async { |
| await indexTestUnit(''' |
| void f() { |
| } |
| '''); |
| _createRefactoringForString('f'); |
| return _assertConditionsFatal( |
| 'Cannot extract the name part of a declaration.'); |
| } |
| |
| Future<void> test_bad_namePartOfDeclaration_variable() async { |
| await indexTestUnit(''' |
| void f() { |
| int vvv = 0; |
| } |
| '''); |
| _createRefactoringForString('vvv'); |
| return _assertConditionsFatal( |
| 'Cannot extract the name part of a declaration.'); |
| } |
| |
| Future<void> test_bad_namePartOfQualified() async { |
| await indexTestUnit(''' |
| class A { |
| var fff; |
| } |
| |
| void f(A a) { |
| a.fff = 1; |
| } |
| '''); |
| _createRefactoringWithSuffix('fff', ' = 1'); |
| return _assertConditionsFatal( |
| 'Can not extract name part of a property access.'); |
| } |
| |
| Future<void> test_bad_newMethodName_notIdentifier() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| print(0); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| refactoring.name = 'bad-name'; |
| // check conditions |
| return _assertConditionsFatal("Method name must not contain '-'."); |
| } |
| |
| Future<void> test_bad_notSameParent() async { |
| await indexTestUnit(''' |
| void f() { |
| while (false) |
| // start |
| { |
| } |
| print(0); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Not all selected statements are enclosed by the same parent statement.'); |
| } |
| |
| Future<void> test_bad_parameterName_duplicate() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| // start |
| int a = v1 + v2; // marker |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // update parameters |
| await refactoring.checkInitialConditions(); |
| { |
| var parameters = _getParametersCopy(); |
| expect(parameters, hasLength(2)); |
| parameters[0].name = 'dup'; |
| parameters[1].name = 'dup'; |
| refactoring.parameters = parameters; |
| } |
| return _assertFinalConditionsError("Parameter 'dup' already exists"); |
| } |
| |
| Future<void> test_bad_parameterName_inUse_function() async { |
| await indexTestUnit(''' |
| void g() { |
| int v1 = 1; |
| int v2 = 2; |
| // start |
| f(v1, v2); |
| // end |
| } |
| f(a, b) {} |
| '''); |
| _createRefactoringForStartEndComments(); |
| // update parameters |
| await refactoring.checkInitialConditions(); |
| { |
| var parameters = _getParametersCopy(); |
| expect(parameters, hasLength(2)); |
| parameters[0].name = 'f'; |
| refactoring.parameters = parameters; |
| } |
| return _assertFinalConditionsError( |
| "'f' is already used as a name in the selected code"); |
| } |
| |
| Future<void> test_bad_parameterName_inUse_localVariable() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| // start |
| int a = v1 + v2; // marker |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // update parameters |
| await refactoring.checkInitialConditions(); |
| { |
| var parameters = _getParametersCopy(); |
| expect(parameters, hasLength(2)); |
| parameters[0].name = 'a'; |
| refactoring.parameters = parameters; |
| } |
| return _assertFinalConditionsError( |
| "'a' is already used as a name in the selected code"); |
| } |
| |
| Future<void> test_bad_parameterName_inUse_method() async { |
| await indexTestUnit(''' |
| class A { |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| // start |
| m(v1, v2); |
| // end |
| } |
| m(a, b) {} |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // update parameters |
| await refactoring.checkInitialConditions(); |
| { |
| var parameters = _getParametersCopy(); |
| expect(parameters, hasLength(2)); |
| parameters[0].name = 'm'; |
| refactoring.parameters = parameters; |
| } |
| return _assertFinalConditionsError( |
| "'m' is already used as a name in the selected code"); |
| } |
| |
| Future<void> test_bad_selectionEndsInSomeNode() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| print(0); |
| print(1); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndString('print(0', 'rint(1)'); |
| return _assertConditionsFatal( |
| 'The selection does not cover a set of statements or an expression. ' |
| 'Extend selection to a valid range.'); |
| } |
| |
| Future<void> test_bad_statements_exit_notAllExecutionFlows() async { |
| await indexTestUnit(''' |
| void f(int p) { |
| // start |
| if (p == 0) { |
| return; |
| } |
| // end |
| print(p); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsError(ExtractMethodRefactoringImpl.ERROR_EXITS); |
| } |
| |
| Future<void> test_bad_statements_return_andAssignsVariable() async { |
| await indexTestUnit(''' |
| int f() { |
| // start |
| var v = 0; |
| return 42; |
| // end |
| print(v); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Ambiguous return value: Selected block contains assignment(s) to ' |
| 'local variables and return statement.'); |
| } |
| |
| Future<void> test_bad_switchCase() async { |
| await indexTestUnit(''' |
| void f() { |
| switch (1) { |
| // start |
| case 0: break; |
| // end |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Selection must either cover whole switch statement ' |
| 'or parts of a single case block.'); |
| } |
| |
| Future<void> test_bad_tokensBetweenLastNodeAndSelectionEnd() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| print(0); |
| print(1); |
| } |
| // end |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'The end of the selection contains characters that do not belong to a statement.'); |
| } |
| |
| Future<void> test_bad_tokensBetweenSelectionStartAndFirstNode() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| print(0); // marker |
| print(1); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndString('); // marker', '// end'); |
| return _assertConditionsFatal( |
| 'The beginning of the selection contains characters that do not belong to a statement.'); |
| } |
| |
| Future<void> test_bad_try_catchBlock_block() async { |
| await indexTestUnit(''' |
| void f() { |
| try |
| {} |
| catch (e) |
| // start |
| {} |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Selection must either cover whole try statement or ' |
| 'parts of try, catch, or finally block.'); |
| } |
| |
| Future<void> test_bad_try_catchBlock_complete() async { |
| await indexTestUnit(''' |
| void f() { |
| try |
| {} |
| // start |
| catch (e) |
| {} |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Selection must either cover whole try statement or ' |
| 'parts of try, catch, or finally block.'); |
| } |
| |
| Future<void> test_bad_try_catchBlock_exception() async { |
| await indexTestUnit(''' |
| void f() { |
| try { |
| } catch ( |
| // start |
| e |
| // end |
| ) { |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Cannot extract the name part of a declaration.'); |
| } |
| |
| Future<void> test_bad_try_finallyBlock() async { |
| await indexTestUnit(''' |
| void f() { |
| try |
| {} |
| finally |
| // start |
| {} |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Selection must either cover whole try statement or ' |
| 'parts of try, catch, or finally block.'); |
| } |
| |
| Future<void> test_bad_try_tryBlock() async { |
| await indexTestUnit(''' |
| void f() { |
| try |
| // start |
| {} |
| // end |
| finally |
| {} |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Selection must either cover whole try statement or ' |
| 'parts of try, catch, or finally block.'); |
| } |
| |
| Future<void> test_bad_typeReference() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 0; |
| } |
| '''); |
| _createRefactoringForString('int'); |
| return _assertConditionsFatal('Cannot extract a single type reference.'); |
| } |
| |
| Future<void> test_bad_variableDeclarationFragment() async { |
| await indexTestUnit(''' |
| void f() { |
| int |
| // start |
| a = 1 |
| // end |
| ,b = 2; |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| 'Cannot extract a variable declaration fragment. Select whole declaration statement.'); |
| } |
| |
| Future<void> test_bad_while_conditionAndBody() async { |
| await indexTestUnit(''' |
| void f() { |
| while |
| // start |
| (false) |
| { |
| } |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| return _assertConditionsFatal( |
| "Operation not applicable to a while statement's expression and body."); |
| } |
| |
| Future<void> test_canExtractGetter_false_closure() async { |
| await indexTestUnit(''' |
| void f() { |
| useFunction((_) => true); |
| } |
| useFunction(filter(String p)) {} |
| '''); |
| _createRefactoringForString('(_) => true'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.canCreateGetter, false); |
| expect(refactoring.createGetter, false); |
| } |
| |
| Future<void> test_canExtractGetter_false_fieldAssignment() async { |
| await indexTestUnit(''' |
| class A { |
| var f; |
| void m() { |
| // start |
| f = 1; |
| // end |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.canCreateGetter, false); |
| expect(refactoring.createGetter, false); |
| } |
| |
| Future<void> test_canExtractGetter_false_hasParameters() async { |
| await indexTestUnit(''' |
| void f(int p) { |
| int a = p + 1; |
| } |
| '''); |
| _createRefactoringForString('p + 1'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.canCreateGetter, false); |
| expect(refactoring.createGetter, false); |
| } |
| |
| Future<void> test_canExtractGetter_false_returnNotUsed_assignment() async { |
| await indexTestUnit(''' |
| var topVar = 0; |
| void f(int p) { |
| topVar = 5; |
| } |
| '''); |
| _createRefactoringForString('topVar = 5'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.canCreateGetter, false); |
| expect(refactoring.createGetter, false); |
| } |
| |
| Future<void> test_canExtractGetter_false_returnNotUsed_noReturn() async { |
| await indexTestUnit(''' |
| var topVar = 0; |
| void f() { |
| // start |
| int a = 1; |
| int b = 2; |
| topVar = a + b; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.canCreateGetter, false); |
| expect(refactoring.createGetter, false); |
| } |
| |
| Future<void> test_canExtractGetter_true() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1 + 2; |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.canCreateGetter, true); |
| expect(refactoring.createGetter, true); |
| } |
| |
| Future<void> test_checkInitialCondition_false_outOfRange_length() async { |
| await indexTestUnit(''' |
| void f() { |
| 1 + 2; |
| } |
| '''); |
| _createRefactoring(0, 1 << 20); |
| var status = await refactoring.checkAllConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL); |
| } |
| |
| Future<void> test_checkInitialCondition_outOfRange_offset() async { |
| await indexTestUnit(''' |
| void f() { |
| 1 + 2; |
| } |
| '''); |
| _createRefactoring(-10, 20); |
| var status = await refactoring.checkAllConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL); |
| } |
| |
| Future<void> test_checkName() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1 + 2; |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // empty |
| refactoring.name = ''; |
| assertRefactoringStatus( |
| refactoring.checkName(), RefactoringProblemSeverity.FATAL, |
| expectedMessage: 'Method name must not be empty.'); |
| // incorrect casing |
| refactoring.name = 'Aaa'; |
| assertRefactoringStatus( |
| refactoring.checkName(), RefactoringProblemSeverity.WARNING, |
| expectedMessage: 'Method name should start with a lowercase letter.'); |
| // starts with digit |
| refactoring.name = '0aa'; |
| assertRefactoringStatus( |
| refactoring.checkName(), RefactoringProblemSeverity.FATAL, |
| expectedMessage: |
| 'Method name must begin with a lowercase letter or underscore.'); |
| // invalid name (quote) |
| refactoring.name = '"'; |
| assertRefactoringStatus( |
| refactoring.checkName(), RefactoringProblemSeverity.FATAL, |
| expectedMessage: "Method name must not contain '\"'."); |
| // OK |
| refactoring.name = 'res'; |
| assertRefactoringStatusOK(refactoring.checkName()); |
| } |
| |
| Future<void> test_closure_asFunction_singleExpression() async { |
| await indexTestUnit(''' |
| process(f(x)) {} |
| void f() { |
| process((x) => x * 2); |
| } |
| '''); |
| _createRefactoringForString('(x) => x * 2'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| process(f(x)) {} |
| void f() { |
| process(res); |
| } |
| |
| res(x) => x * 2; |
| '''); |
| } |
| |
| Future<void> test_closure_asFunction_statements() async { |
| await indexTestUnit(''' |
| process(f(x)) {} |
| void f() { |
| process((x) { |
| print(x); |
| return x * 2; |
| }); // marker |
| } |
| '''); |
| _createRefactoringForStartEndString('(x) {', '); // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| process(f(x)) {} |
| void f() { |
| process(res); // marker |
| } |
| |
| res(x) { |
| print(x); |
| return x * 2; |
| } |
| '''); |
| } |
| |
| Future<void> test_closure_asMethod_statements() async { |
| await indexTestUnit(''' |
| process(f(x)) {} |
| class A { |
| int k = 2; |
| void f() { |
| process((x) { |
| print(x); |
| return x * k; |
| }); // marker |
| } |
| } |
| '''); |
| _createRefactoringForStartEndString('(x) {', '); // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| process(f(x)) {} |
| class A { |
| int k = 2; |
| void f() { |
| process(res); // marker |
| } |
| |
| res(x) { |
| print(x); |
| return x * k; |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_closure_atArgumentName() async { |
| await indexTestUnit(''' |
| void process({int fff(int x)?}) {} |
| class C { |
| void f() { |
| process(fff: (int x) => x * 2); |
| } |
| } |
| '''); |
| _createRefactoring(findOffset('ff: (int x)'), 0); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void process({int fff(int x)?}) {} |
| class C { |
| void f() { |
| process(fff: res); |
| } |
| |
| int res(int x) => x * 2; |
| } |
| '''); |
| } |
| |
| Future<void> test_closure_atParameters() async { |
| await indexTestUnit(''' |
| void process(num f(int x)) {} |
| class C { |
| void f() { |
| process((int x) => x * 2); |
| } |
| } |
| '''); |
| _createRefactoring(findOffset('x) =>'), 0); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void process(num f(int x)) {} |
| class C { |
| void f() { |
| process(res); |
| } |
| |
| num res(int x) => x * 2; |
| } |
| '''); |
| } |
| |
| Future<void> test_closure_bad_referencesLocalVariable() async { |
| await indexTestUnit(''' |
| process(f(x)) {} |
| void f() { |
| int k = 2; |
| process((x) => x * k); |
| } |
| '''); |
| _createRefactoringForString('(x) => x * k'); |
| // check |
| var status = await refactoring.checkInitialConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL, |
| expectedMessage: |
| 'Cannot extract closure as method, it references 1 external variable.'); |
| } |
| |
| Future<void> test_closure_bad_referencesParameter() async { |
| await indexTestUnit(''' |
| process(f(x)) {} |
| void f(int k) { |
| process((x) => x * k); |
| } |
| '''); |
| _createRefactoringForString('(x) => x * k'); |
| // check |
| var status = await refactoring.checkInitialConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL, |
| expectedMessage: |
| 'Cannot extract closure as method, it references 1 external variable.'); |
| } |
| |
| Future<void> test_fromTopLevelVariableInitializerClosure() async { |
| await indexTestUnit(''' |
| var X = 1; |
| |
| dynamic Y = () { |
| return 1 + X; |
| }; |
| '''); |
| _createRefactoringForString('1 + X'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| var X = 1; |
| |
| dynamic Y = () { |
| return res(); |
| }; |
| |
| int res() => 1 + X; |
| '''); |
| } |
| |
| Future<void> test_getExtractGetter_expression_true_binaryExpression() async { |
| await indexTestUnit(''' |
| void f() { |
| print(1 + 2); |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.createGetter, true); |
| } |
| |
| Future<void> test_getExtractGetter_expression_true_literal() async { |
| await indexTestUnit(''' |
| void f() { |
| print(42); |
| } |
| '''); |
| _createRefactoringForString('42'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.createGetter, true); |
| } |
| |
| Future<void> |
| test_getExtractGetter_expression_true_prefixedExpression() async { |
| await indexTestUnit(''' |
| void f() { |
| print(!true); |
| } |
| '''); |
| _createRefactoringForString('!true'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.createGetter, true); |
| } |
| |
| Future<void> |
| test_getExtractGetter_expression_true_prefixedIdentifier() async { |
| await indexTestUnit(''' |
| void f() { |
| print(myValue.isEven); |
| } |
| int get myValue => 42; |
| '''); |
| _createRefactoringForString('myValue.isEven'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.createGetter, true); |
| } |
| |
| Future<void> test_getExtractGetter_expression_true_propertyAccess() async { |
| await indexTestUnit(''' |
| void f() { |
| print(1.isEven); |
| } |
| '''); |
| _createRefactoringForString('1.isEven'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.createGetter, true); |
| } |
| |
| Future<void> test_getExtractGetter_statements() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| int v = 0; |
| // end |
| print(v); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.createGetter, false); |
| } |
| |
| Future<void> test_getRefactoringName_function() async { |
| await indexTestUnit(''' |
| void f() { |
| print(1 + 2); |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| expect(refactoring.refactoringName, 'Extract Function'); |
| } |
| |
| Future<void> test_getRefactoringName_method() async { |
| await indexTestUnit(''' |
| class A { |
| void f() { |
| print(1 + 2); |
| } |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| expect(refactoring.refactoringName, 'Extract Method'); |
| } |
| |
| Future<void> test_isAvailable_false_functionName() async { |
| await indexTestUnit(''' |
| void f() {} |
| '''); |
| _createRefactoringForString('f'); |
| expect(refactoring.isAvailable(), isFalse); |
| } |
| |
| Future<void> test_isAvailable_true() async { |
| await indexTestUnit(''' |
| void f() { |
| 1 + 2; |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| expect(refactoring.isAvailable(), isTrue); |
| } |
| |
| Future<void> test_names_singleExpression() async { |
| await indexTestUnit(''' |
| class TreeItem {} |
| TreeItem getSelectedItem() => throw 0; |
| process(my) {} |
| void f() { |
| process(getSelectedItem()); // marker |
| int treeItem = 0; |
| } |
| '''); |
| _createRefactoringWithSuffix('getSelectedItem()', '); // marker'); |
| // check names |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.names, |
| unorderedEquals(['selectedItem', 'item', 'my', 'treeItem2'])); |
| } |
| |
| Future<void> test_offsets_lengths() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1 + 2; |
| int b = 1 + 2; |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // apply refactoring |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.offsets, |
| unorderedEquals([findOffset('1 + 2'), findOffset('1 + 2')])); |
| expect(refactoring.lengths, unorderedEquals([5, 6])); |
| } |
| |
| Future<void> test_returnType_closure() async { |
| await indexTestUnit(''' |
| process(f(x)) {} |
| void f() { |
| process((x) => x * 2); |
| } |
| '''); |
| _createRefactoringForString('(x) => x * 2'); |
| // do check |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.returnType, ''); |
| } |
| |
| Future<void> test_returnType_expression() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1 + 2; |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // do check |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.returnType, 'int'); |
| } |
| |
| Future<void> test_returnType_mixInterfaceFunction() async { |
| await indexTestUnit(''' |
| Object f() { |
| // start |
| if (true) { |
| return 1; |
| } else { |
| return () {}; |
| } |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // do check |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.returnType, 'Object'); |
| } |
| |
| Future<void> test_returnType_statements() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| double v = 5.0; |
| // end |
| print(v); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // do check |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.returnType, 'double'); |
| } |
| |
| Future<void> test_returnType_statements_nullMix() async { |
| await indexTestUnit(''' |
| f(bool p) { |
| // start |
| if (p) { |
| return 42; |
| } |
| return null; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // do check |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.returnType, 'int?'); |
| } |
| |
| Future<void> test_returnType_statements_void() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| print(42); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // do check |
| await refactoring.checkInitialConditions(); |
| expect(refactoring.returnType, 'void'); |
| } |
| |
| Future<void> test_setExtractGetter() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1 + 2; |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // apply refactoring |
| await assertRefactoringConditionsOK(); |
| expect(refactoring.canCreateGetter, true); |
| expect(refactoring.createGetter, true); |
| refactoringChange = await refactoring.createChange(); |
| assertTestChangeResult(''' |
| void f() { |
| int a = res; |
| } |
| |
| int get res => 1 + 2; |
| '''); |
| } |
| |
| Future<void> test_singleExpression() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1 + 2; |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int a = res(); |
| } |
| |
| int res() => 1 + 2; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_cascade() async { |
| await indexTestUnit(''' |
| void f() { |
| String s = ''; |
| var v = s..length; |
| } |
| '''); |
| _createRefactoringForString('s..length'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| String s = ''; |
| var v = res(s); |
| } |
| |
| String res(String s) => s..length; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_coveringExpression() async { |
| await indexTestUnit(''' |
| void f(int n) { |
| var v = new FooBar(n); |
| } |
| |
| class FooBar { |
| FooBar(int count); |
| } |
| '''); |
| _createRefactoringForStringOffset('Bar(n);'); |
| return _assertSuccessfulRefactoring(''' |
| void f(int n) { |
| var v = res(n); |
| } |
| |
| FooBar res(int n) => new FooBar(n); |
| |
| class FooBar { |
| FooBar(int count); |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_dynamic() async { |
| await indexTestUnit(''' |
| dynaFunction() {} |
| void f() { |
| var v = dynaFunction(); // marker |
| } |
| '''); |
| _createRefactoringWithSuffix('dynaFunction()', '; // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| dynaFunction() {} |
| void f() { |
| var v = res(); // marker |
| } |
| |
| res() => dynaFunction(); |
| '''); |
| } |
| |
| Future<void> test_singleExpression_hasAwait() async { |
| await indexTestUnit(''' |
| import 'dart:async'; |
| Future<int> getValue() async => 42; |
| void f() async { |
| int v = await getValue(); |
| print(v); |
| } |
| '''); |
| _createRefactoringForString('await getValue()'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'dart:async'; |
| Future<int> getValue() async => 42; |
| void f() async { |
| int v = await res(); |
| print(v); |
| } |
| |
| Future<int> res() async => await getValue(); |
| '''); |
| } |
| |
| Future<void> test_singleExpression_ignore_assignmentLeftHandSize() async { |
| await indexTestUnit(''' |
| void f() { |
| getButton().text = 'txt'; |
| print(getButton().text); // marker |
| } |
| getButton() {} |
| '''); |
| _createRefactoringWithSuffix('getButton().text', '); // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| getButton().text = 'txt'; |
| print(res()); // marker |
| } |
| |
| res() => getButton().text; |
| getButton() {} |
| '''); |
| } |
| |
| Future<void> test_singleExpression_occurrences() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int positiveA = v1 + v2; // marker |
| int positiveB = v2 + v3; |
| int positiveC = v1 + v2; |
| int positiveD = v1/*abc*/ + v2; |
| int negA = 1 + 2; |
| int negB = 1 + v2; |
| int negC = v1 + 2; |
| int negD = v1 * v2; |
| } |
| '''); |
| _createRefactoringWithSuffix('v1 + v2', '; // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int positiveA = res(v1, v2); // marker |
| int positiveB = res(v2, v3); |
| int positiveC = res(v1, v2); |
| int positiveD = res(v1, v2); |
| int negA = 1 + 2; |
| int negB = 1 + v2; |
| int negC = v1 + 2; |
| int negD = v1 * v2; |
| } |
| |
| int res(int v1, int v2) => v1 + v2; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_occurrences_disabled() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = v1 + v2; // marker |
| int b = v2 + v3; |
| } |
| '''); |
| _createRefactoringWithSuffix('v1 + v2', '; // marker'); |
| refactoring.extractAll = false; |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = res(v1, v2); // marker |
| int b = v2 + v3; |
| } |
| |
| int res(int v1, int v2) => v1 + v2; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_occurrences_inClassOnly() async { |
| await indexTestUnit(''' |
| class A { |
| myMethod() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = v1 + v2; // marker |
| } |
| } |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int negA = v1 + v2; |
| } |
| '''); |
| _createRefactoringWithSuffix('v1 + v2', '; // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class A { |
| myMethod() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = res(v1, v2); // marker |
| } |
| |
| int res(int v1, int v2) => v1 + v2; |
| } |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int negA = v1 + v2; |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_occurrences_incompatibleTypes() async { |
| await indexTestUnit(''' |
| void f() { |
| int x = 1; |
| String y = 'foo'; |
| print(x.toString()); |
| print(y.toString()); |
| } |
| '''); |
| _createRefactoringForString('x.toString()'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int x = 1; |
| String y = 'foo'; |
| print(res(x)); |
| print(y.toString()); |
| } |
| |
| String res(int x) => x.toString(); |
| '''); |
| } |
| |
| Future<void> test_singleExpression_occurrences_inWholeUnit() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = v1 + v2; // marker |
| } |
| class A { |
| myMethod() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveB = v1 + v2; |
| } |
| } |
| '''); |
| _createRefactoringWithSuffix('v1 + v2', '; // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = res(v1, v2); // marker |
| } |
| |
| int res(int v1, int v2) => v1 + v2; |
| class A { |
| myMethod() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveB = res(v1, v2); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_parameter_functionTypeAlias() async { |
| await indexTestUnit(''' |
| typedef R Foo<S, R>(S s); |
| void f(Foo<String, int> foo, String s) { |
| int a = foo(s); |
| } |
| '''); |
| _createRefactoringForString('foo(s)'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| typedef R Foo<S, R>(S s); |
| void f(Foo<String, int> foo, String s) { |
| int a = res(foo, s); |
| } |
| |
| int res(Foo<String, int> foo, String s) => foo(s); |
| '''); |
| } |
| |
| Future<void> test_singleExpression_returnType_importLibrary() async { |
| _addLibraryReturningAsync(); |
| await indexTestUnit(''' |
| import 'asyncLib.dart'; |
| void f() { |
| var a = newCompleter(); |
| } |
| '''); |
| _createRefactoringForString('newCompleter()'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'asyncLib.dart'; |
| import 'dart:async'; |
| void f() { |
| var a = res(); |
| } |
| |
| Completer<int> res() => newCompleter(); |
| '''); |
| } |
| |
| Future<void> test_singleExpression_returnTypeGeneric() async { |
| await indexTestUnit(''' |
| void f() { |
| var v = <String>[]; |
| } |
| '''); |
| _createRefactoringForString('<String>[]'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| var v = res(); |
| } |
| |
| List<String> res() => <String>[]; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_returnTypePrefix() async { |
| await indexTestUnit(''' |
| import 'dart:math' as pref; |
| void f() { |
| var v = new pref.Random(); |
| } |
| '''); |
| _createRefactoringForString('new pref.Random()'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'dart:math' as pref; |
| void f() { |
| var v = res(); |
| } |
| |
| pref.Random res() => new pref.Random(); |
| '''); |
| } |
| |
| Future<void> |
| test_singleExpression_staticContext_extractFromInitializer() async { |
| await indexTestUnit(''' |
| class A { |
| A(int v) {} |
| } |
| class B extends A { |
| B() : super(1 + 2) {} |
| } |
| '''); |
| _createRefactoringForString('1 + 2'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class A { |
| A(int v) {} |
| } |
| class B extends A { |
| B() : super(res()) {} |
| |
| static int res() => 1 + 2; |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_staticContext_extractFromInstance() async { |
| await indexTestUnit(''' |
| class A { |
| instanceMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = v1 + v2; // marker |
| } |
| instanceMethodB() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveB = v1 + v2; |
| } |
| static staticMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = v1 + v2; |
| } |
| } |
| '''); |
| _createRefactoringWithSuffix('v1 + v2', '; // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class A { |
| instanceMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = res(v1, v2); // marker |
| } |
| |
| static int res(int v1, int v2) => v1 + v2; |
| instanceMethodB() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveB = res(v1, v2); |
| } |
| static staticMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = res(v1, v2); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_staticContext_extractFromStatic() async { |
| await indexTestUnit(''' |
| class A { |
| static staticMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = v1 + v2; // marker |
| } |
| static staticMethodB() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveB = v1 + v2; |
| } |
| instanceMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = v1 + v2; |
| } |
| } |
| '''); |
| _createRefactoringWithSuffix('v1 + v2', '; // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class A { |
| static staticMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = res(v1, v2); // marker |
| } |
| |
| static int res(int v1, int v2) => v1 + v2; |
| static staticMethodB() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveB = res(v1, v2); |
| } |
| instanceMethodA() { |
| int v1 = 1; |
| int v2 = 2; |
| int positiveA = res(v1, v2); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_staticContext_hasInInitializer() async { |
| await indexTestUnit(''' |
| class A { |
| A(int v) {} |
| } |
| class B extends A { |
| B() : super(1 + 2) {} |
| foo() { |
| print(1 + 2); // marker |
| } |
| } |
| '''); |
| _createRefactoringWithSuffix('1 + 2', '); // marker'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class A { |
| A(int v) {} |
| } |
| class B extends A { |
| B() : super(res()) {} |
| foo() { |
| print(res()); // marker |
| } |
| |
| static int res() => 1 + 2; |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_usesParameter() async { |
| await indexTestUnit(''' |
| fooA(int a1) { |
| int a2 = 2; |
| int a = a1 + a2; |
| } |
| fooB(int b1) { |
| int b2 = 2; |
| int b = b1 + b2; |
| } |
| '''); |
| _createRefactoringForString('a1 + a2'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| fooA(int a1) { |
| int a2 = 2; |
| int a = res(a1, a2); |
| } |
| |
| int res(int a1, int a2) => a1 + a2; |
| fooB(int b1) { |
| int b2 = 2; |
| int b = res(b1, b2); |
| } |
| '''); |
| } |
| |
| Future<void> test_singleExpression_withVariables() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int a = v1 + v2 + v1; |
| } |
| '''); |
| _createRefactoringForString('v1 + v2 + v1'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int a = res(v1, v2); |
| } |
| |
| int res(int v1, int v2) => v1 + v2 + v1; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_withVariables_doRename() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = v1 + v2 + v1; // marker |
| int b = v2 + v3 + v2; |
| } |
| '''); |
| _createRefactoringForString('v1 + v2 + v1'); |
| // apply refactoring |
| await refactoring.checkInitialConditions(); |
| { |
| var parameters = _getParametersCopy(); |
| expect(parameters, hasLength(2)); |
| expect(parameters[0].name, 'v1'); |
| expect(parameters[1].name, 'v2'); |
| parameters[0].name = 'par1'; |
| parameters[1].name = 'param2'; |
| refactoring.parameters = parameters; |
| } |
| await assertRefactoringFinalConditionsOK(); |
| refactoring.createGetter = false; |
| return _assertRefactoringChange(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = res(v1, v2); // marker |
| int b = res(v2, v3); |
| } |
| |
| int res(int par1, int param2) => par1 + param2 + par1; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_withVariables_doReorder() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = v1 + v2; // marker |
| int b = v2 + v3; |
| } |
| '''); |
| _createRefactoringForString('v1 + v2'); |
| // apply refactoring |
| await refactoring.checkInitialConditions(); |
| { |
| var parameters = _getParametersCopy(); |
| expect(parameters, hasLength(2)); |
| expect(parameters[0].name, 'v1'); |
| expect(parameters[1].name, 'v2'); |
| var parameter = parameters.removeAt(1); |
| parameters.insert(0, parameter); |
| refactoring.parameters = parameters; |
| } |
| await assertRefactoringFinalConditionsOK(); |
| refactoring.createGetter = false; |
| return _assertRefactoringChange(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = res(v2, v1); // marker |
| int b = res(v3, v2); |
| } |
| |
| int res(int v2, int v1) => v1 + v2; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_withVariables_namedExpression() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int a = process(arg: v1 + v2); |
| } |
| process({arg}) {} |
| '''); |
| _createRefactoringForString('process(arg: v1 + v2)'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int a = res(v1, v2); |
| } |
| |
| res(int v1, int v2) => process(arg: v1 + v2); |
| process({arg}) {} |
| '''); |
| } |
| |
| Future<void> test_singleExpression_withVariables_newType() async { |
| await indexTestUnit(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = v1 + v2 + v3; |
| } |
| '''); |
| _createRefactoringForString('v1 + v2 + v3'); |
| // apply refactoring |
| await refactoring.checkInitialConditions(); |
| { |
| var parameters = _getParametersCopy(); |
| expect(parameters, hasLength(3)); |
| expect(parameters[0].name, 'v1'); |
| expect(parameters[1].name, 'v2'); |
| expect(parameters[2].name, 'v3'); |
| parameters[0].type = 'num'; |
| parameters[1].type = 'dynamic'; |
| parameters[2].type = ''; |
| refactoring.parameters = parameters; |
| } |
| await assertRefactoringFinalConditionsOK(); |
| refactoring.createGetter = false; |
| return _assertRefactoringChange(''' |
| void f() { |
| int v1 = 1; |
| int v2 = 2; |
| int v3 = 3; |
| int a = res(v1, v2, v3); |
| } |
| |
| int res(num v1, v2, v3) => v1 + v2 + v3; |
| '''); |
| } |
| |
| Future<void> test_singleExpression_withVariables_useBestType() async { |
| await indexTestUnit(''' |
| void f() { |
| var v1 = 1; |
| var v2 = 2; |
| var a = v1 + v2 + v1; // marker |
| } |
| '''); |
| _createRefactoringForString('v1 + v2 + v1'); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| var v1 = 1; |
| var v2 = 2; |
| var a = res(v1, v2); // marker |
| } |
| |
| int res(int v1, int v2) => v1 + v2 + v1; |
| '''); |
| } |
| |
| Future<void> test_statements_assignment() async { |
| await indexTestUnit(''' |
| void f() { |
| int v; |
| // start |
| v = 5; |
| // end |
| print(v); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int v; |
| // start |
| v = res(v); |
| // end |
| print(v); |
| } |
| |
| int res(int v) { |
| v = 5; |
| return v; |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_changeIndentation() async { |
| await indexTestUnit(''' |
| void f() { |
| { |
| // start |
| if (true) { |
| print(0); |
| } |
| // end |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| { |
| // start |
| res(); |
| // end |
| } |
| } |
| |
| void res() { |
| if (true) { |
| print(0); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_changeIndentation_multilineString() async { |
| await indexTestUnit(''' |
| void f() { |
| { |
| // start |
| print(""" |
| first line |
| second line |
| """); |
| // end |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| { |
| // start |
| res(); |
| // end |
| } |
| } |
| |
| void res() { |
| print(""" |
| first line |
| second line |
| """); |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_definesVariable_notUsedOutside() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1; |
| int b = 1; |
| // start |
| int v = a + b; |
| print(v); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int a = 1; |
| int b = 1; |
| // start |
| res(a, b); |
| // end |
| } |
| |
| void res(int a, int b) { |
| int v = a + b; |
| print(v); |
| } |
| '''); |
| } |
| |
| Future<void> |
| test_statements_definesVariable_oneUsedOutside_assignment() async { |
| await indexTestUnit(''' |
| myFunctionA() { |
| int a = 1; |
| // start |
| a += 10; |
| // end |
| print(a); |
| } |
| myFunctionB() { |
| int b = 2; |
| b += 10; |
| print(b); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| myFunctionA() { |
| int a = 1; |
| // start |
| a = res(a); |
| // end |
| print(a); |
| } |
| |
| int res(int a) { |
| a += 10; |
| return a; |
| } |
| myFunctionB() { |
| int b = 2; |
| b = res(b); |
| print(b); |
| } |
| '''); |
| } |
| |
| Future<void> |
| test_statements_definesVariable_oneUsedOutside_declaration() async { |
| await indexTestUnit(''' |
| myFunctionA() { |
| int a = 1; |
| int b = 2; |
| // start |
| int v1 = a + b; |
| // end |
| print(v1); |
| } |
| myFunctionB() { |
| int a = 3; |
| int b = 4; |
| int v2 = a + b; |
| print(v2); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| myFunctionA() { |
| int a = 1; |
| int b = 2; |
| // start |
| int v1 = res(a, b); |
| // end |
| print(v1); |
| } |
| |
| int res(int a, int b) { |
| int v1 = a + b; |
| return v1; |
| } |
| myFunctionB() { |
| int a = 3; |
| int b = 4; |
| int v2 = res(a, b); |
| print(v2); |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_definesVariable_twoUsedOutside() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| int varA = 1; |
| int varB = 2; |
| // end |
| int v = varA + varB; |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // check conditions |
| var status = await refactoring.checkInitialConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL); |
| } |
| |
| Future<void> test_statements_duplicate_absolutelySame() async { |
| await indexTestUnit(''' |
| myFunctionA() { |
| print(0); |
| print(1); |
| } |
| myFunctionB() { |
| // start |
| print(0); |
| print(1); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| myFunctionA() { |
| res(); |
| } |
| myFunctionB() { |
| // start |
| res(); |
| // end |
| } |
| |
| void res() { |
| print(0); |
| print(1); |
| } |
| '''); |
| } |
| |
| Future<void> |
| test_statements_duplicate_declaresDifferentlyNamedVariable() async { |
| await indexTestUnit(''' |
| myFunctionA() { |
| int varA = 1; |
| print(varA); |
| } |
| myFunctionB() { |
| // start |
| int varB = 1; |
| print(varB); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| myFunctionA() { |
| res(); |
| } |
| myFunctionB() { |
| // start |
| res(); |
| // end |
| } |
| |
| void res() { |
| int varB = 1; |
| print(varB); |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_dynamic() async { |
| await indexTestUnit(''' |
| dynaFunction(p) => 0; |
| void f() { |
| // start |
| var a = 1; |
| var v = dynaFunction(a); |
| // end |
| print(v); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| dynaFunction(p) => 0; |
| void f() { |
| // start |
| var v = res(); |
| // end |
| print(v); |
| } |
| |
| res() { |
| var a = 1; |
| var v = dynaFunction(a); |
| return v; |
| } |
| '''); |
| } |
| |
| /// We should always add ";" when invoke method with extracted statements. |
| Future<void> test_statements_endsWithBlock() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| if (true) { |
| print(0); |
| } |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| // start |
| res(); |
| // end |
| } |
| |
| void res() { |
| if (true) { |
| print(0); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_exit_throws() async { |
| await indexTestUnit(''' |
| void f(int p) { |
| // start |
| if (p == 0) { |
| return; |
| } |
| throw 'boo!'; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| await assertRefactoringConditionsOK(); |
| } |
| |
| Future<void> test_statements_hasAwait_dynamicReturnType() async { |
| await indexTestUnit(''' |
| import 'dart:async'; |
| Future getValue() async => 42; |
| void f() async { |
| // start |
| var v = await getValue(); |
| // end |
| print(v); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'dart:async'; |
| Future getValue() async => 42; |
| void f() async { |
| // start |
| var v = await res(); |
| // end |
| print(v); |
| } |
| |
| Future<dynamic> res() async { |
| var v = await getValue(); |
| return v; |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_hasAwait_expression() async { |
| await indexTestUnit(''' |
| import 'dart:async'; |
| Future<int> getValue() async => 42; |
| void f() async { |
| // start |
| int v = await getValue(); |
| v += 2; |
| // end |
| print(v); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'dart:async'; |
| Future<int> getValue() async => 42; |
| void f() async { |
| // start |
| int v = await res(); |
| // end |
| print(v); |
| } |
| |
| Future<int> res() async { |
| int v = await getValue(); |
| v += 2; |
| return v; |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_hasAwait_forEach() async { |
| await indexTestUnit(''' |
| import 'dart:async'; |
| Stream<int> getValueStream() => throw 0; |
| void f() async { |
| // start |
| int sum = 0; |
| await for (int v in getValueStream()) { |
| sum += v; |
| } |
| // end |
| print(sum); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'dart:async'; |
| Stream<int> getValueStream() => throw 0; |
| void f() async { |
| // start |
| int sum = await res(); |
| // end |
| print(sum); |
| } |
| |
| Future<int> res() async { |
| int sum = 0; |
| await for (int v in getValueStream()) { |
| sum += v; |
| } |
| return sum; |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_hasAwait_voidReturnType() async { |
| await indexTestUnit(''' |
| import 'dart:async'; |
| Future<int> getValue() async => 42; |
| void f() async { |
| // start |
| int v = await getValue(); |
| print(v); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'dart:async'; |
| Future<int> getValue() async => 42; |
| void f() async { |
| // start |
| await res(); |
| // end |
| } |
| |
| Future<void> res() async { |
| int v = await getValue(); |
| print(v); |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_inSwitchMember() async { |
| await indexTestUnit(''' |
| class A { |
| foo(int p) { |
| switch (p) { |
| case 0: |
| // start |
| print(0); |
| // end |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class A { |
| foo(int p) { |
| switch (p) { |
| case 0: |
| // start |
| res(); |
| // end |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void res() { |
| print(0); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_method() async { |
| await indexTestUnit(''' |
| class A { |
| foo() { |
| // start |
| print(0); |
| // end |
| } |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class A { |
| foo() { |
| // start |
| res(); |
| // end |
| } |
| |
| void res() { |
| print(0); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_noDuplicates() async { |
| await indexTestUnit(''' |
| void f() { |
| int a = 1; |
| int b = 1; |
| // start |
| print(a); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| int a = 1; |
| int b = 1; |
| // start |
| res(a); |
| // end |
| } |
| |
| void res(int a) { |
| print(a); |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_parameters_ignoreInnerPropagatedType() async { |
| await indexTestUnit(''' |
| void f(Object x) { |
| // start |
| if (x is int) { |
| print('int'); |
| } |
| if (x is bool) { |
| print('bool'); |
| } |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f(Object x) { |
| // start |
| res(x); |
| // end |
| } |
| |
| void res(Object x) { |
| if (x is int) { |
| print('int'); |
| } |
| if (x is bool) { |
| print('bool'); |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_parameters_importType() async { |
| _addLibraryReturningAsync(); |
| await indexTestUnit(''' |
| import 'asyncLib.dart'; |
| void f() { |
| var v = newCompleter(); |
| // start |
| print(v); |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| import 'asyncLib.dart'; |
| import 'dart:async'; |
| void f() { |
| var v = newCompleter(); |
| // start |
| res(v); |
| // end |
| } |
| |
| void res(Completer<int> v) { |
| print(v); |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_parameters_localFunction() async { |
| await indexTestUnit(''' |
| class C { |
| int f(int a) { |
| int callback(int x, int y) => x + a; |
| int b = a + 1; |
| // start |
| int c = callback(b, 2); |
| // end |
| int d = c + 1; |
| return d; |
| } |
| }'''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| class C { |
| int f(int a) { |
| int callback(int x, int y) => x + a; |
| int b = a + 1; |
| // start |
| int c = res(callback, b); |
| // end |
| int d = c + 1; |
| return d; |
| } |
| |
| int res(int callback(int x, int y), int b) { |
| int c = callback(b, 2); |
| return c; |
| } |
| }'''); |
| } |
| |
| Future<void> test_statements_parameters_noLocalVariableConflict() async { |
| await indexTestUnit(''' |
| int f(int x) { |
| int y = x + 1; |
| // start |
| if (y % 2 == 0) { |
| int y = x + 2; |
| return y; |
| } else { |
| return y; |
| } |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| await assertRefactoringConditionsOK(); |
| } |
| |
| Future<void> test_statements_return_last() async { |
| await indexTestUnit(''' |
| int f() { |
| // start |
| int v = 5; |
| return v + 1; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| int f() { |
| // start |
| return res(); |
| // end |
| } |
| |
| int res() { |
| int v = 5; |
| return v + 1; |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_return_multiple_ifElse() async { |
| await indexTestUnit(''' |
| num f(bool b) { |
| // start |
| if (b) { |
| return 1; |
| } else { |
| return 2.0; |
| } |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| num f(bool b) { |
| // start |
| return res(b); |
| // end |
| } |
| |
| num res(bool b) { |
| if (b) { |
| return 1; |
| } else { |
| return 2.0; |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_return_multiple_ifThen() async { |
| await indexTestUnit(''' |
| num f(bool b) { |
| // start |
| if (b) { |
| return 1; |
| } |
| return 2.0; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| num f(bool b) { |
| // start |
| return res(b); |
| // end |
| } |
| |
| num res(bool b) { |
| if (b) { |
| return 1; |
| } |
| return 2.0; |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_return_multiple_ignoreInFunction() async { |
| await indexTestUnit(''' |
| int f() { |
| // start |
| localFunction() { |
| return 'abc'; |
| } |
| return 42; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| int f() { |
| // start |
| return res(); |
| // end |
| } |
| |
| int res() { |
| localFunction() { |
| return 'abc'; |
| } |
| return 42; |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_return_multiple_interfaceFunction() async { |
| await indexTestUnit(''' |
| f(bool b) { |
| // start |
| if (b) { |
| return 1; |
| } |
| return () {}; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| f(bool b) { |
| // start |
| return res(b); |
| // end |
| } |
| |
| Object res(bool b) { |
| if (b) { |
| return 1; |
| } |
| return () {}; |
| } |
| '''); |
| } |
| |
| Future<void> |
| test_statements_return_multiple_sameElementDifferentTypeArgs() async { |
| await indexTestUnit(''' |
| f(bool b) { |
| // start |
| if (b) { |
| print(true); |
| return <int>[]; |
| } else { |
| print(false); |
| return <String>[]; |
| } |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| f(bool b) { |
| // start |
| return res(b); |
| // end |
| } |
| |
| List<Object> res(bool b) { |
| if (b) { |
| print(true); |
| return <int>[]; |
| } else { |
| print(false); |
| return <String>[]; |
| } |
| } |
| '''); |
| } |
| |
| Future<void> test_statements_return_single() async { |
| await indexTestUnit(''' |
| int f() { |
| // start |
| return 42; |
| // end |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| int f() { |
| // start |
| return res(); |
| // end |
| } |
| |
| int res() { |
| return 42; |
| } |
| '''); |
| } |
| |
| /// We have 3 identical statements, but select only 2. |
| /// This should not cause problems. |
| Future<void> test_statements_twoOfThree() async { |
| await indexTestUnit(''' |
| void f() { |
| // start |
| print(0); |
| print(0); |
| // end |
| print(0); |
| } |
| '''); |
| _createRefactoringForStartEndComments(); |
| // apply refactoring |
| return _assertSuccessfulRefactoring(''' |
| void f() { |
| // start |
| res(); |
| // end |
| print(0); |
| } |
| |
| void res() { |
| print(0); |
| print(0); |
| } |
| '''); |
| } |
| |
| void _addLibraryReturningAsync() { |
| addSource('/home/test/lib/asyncLib.dart', r''' |
| import 'dart:async'; |
| |
| Completer<int> newCompleter() => null; |
| '''); |
| } |
| |
| Future _assertConditionsError(String message) async { |
| var status = await refactoring.checkAllConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR, |
| expectedMessage: message); |
| } |
| |
| Future _assertConditionsFatal(String message) async { |
| var status = await refactoring.checkAllConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL, |
| expectedMessage: message); |
| } |
| |
| Future _assertFinalConditionsError(String message) async { |
| var status = await refactoring.checkFinalConditions(); |
| assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR, |
| expectedMessage: message); |
| } |
| |
| Future _assertRefactoringChange(String expectedCode) async { |
| var refactoringChange = await refactoring.createChange(); |
| this.refactoringChange = refactoringChange; |
| assertTestChangeResult(expectedCode); |
| } |
| |
| /// Checks that all conditions are OK and the result of applying the [Change] |
| /// to [testUnit] is [expectedCode]. |
| Future _assertSuccessfulRefactoring(String expectedCode) async { |
| await assertRefactoringConditionsOK(); |
| refactoring.createGetter = false; |
| return _assertRefactoringChange(expectedCode); |
| } |
| |
| void _createRefactoring(int offset, int length) { |
| refactoring = ExtractMethodRefactoringImpl( |
| searchEngine, testAnalysisResult, offset, length); |
| refactoring.name = 'res'; |
| } |
| |
| void _createRefactoringForStartEndComments() { |
| final eol = testCode.contains('\r\n') ? '\r\n' : '\r'; |
| var offset = findEnd('// start') + eol.length; |
| var end = findOffset('// end'); |
| _createRefactoring(offset, end - offset); |
| } |
| |
| void _createRefactoringForStartEndString( |
| String startSearch, String endSearch) { |
| var offset = findOffset(startSearch); |
| var end = findOffset(endSearch); |
| _createRefactoring(offset, end - offset); |
| } |
| |
| /// Creates a new refactoring in [refactoring] for the selection range of the |
| /// given [search] pattern. |
| void _createRefactoringForString(String search) { |
| var offset = findOffset(search); |
| var length = search.length; |
| _createRefactoring(offset, length); |
| } |
| |
| /// Creates a new refactoring in [refactoring] at the offset of the given |
| /// [search] pattern, and with `0` length. |
| void _createRefactoringForStringOffset(String search) { |
| var offset = findOffset(search); |
| _createRefactoring(offset, 0); |
| } |
| |
| void _createRefactoringWithSuffix(String selectionSearch, String suffix) { |
| var offset = findOffset(selectionSearch + suffix); |
| var length = selectionSearch.length; |
| _createRefactoring(offset, length); |
| } |
| |
| /// Returns a deep copy of [refactoring] parameters. |
| /// There was a bug masked by updating parameter instances shared between the |
| /// refactoring and the test. |
| List<RefactoringMethodParameter> _getParametersCopy() { |
| return refactoring.parameters.map((p) { |
| return RefactoringMethodParameter(p.kind, p.type, p.name, id: p.id); |
| }).toList(); |
| } |
| } |