blob: 27a8207ac24c031a5028579abc69981297415b86 [file] [log] [blame]
// 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:analyzer/src/generated/source.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_rename.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(RenameClassMemberTest);
});
}
@reflectiveTest
class RenameClassMemberTest extends RenameRefactoringTest {
Future<void> test_checkFinalConditions_classNameConflict_sameClass() async {
await indexTestUnit('''
class NewName {
void test() {}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'NewName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Renamed method has the same name as the declaring class 'NewName'.",
expectedContextSearch: 'test() {}');
}
Future<void> test_checkFinalConditions_classNameConflict_subClass() async {
await indexTestUnit('''
class A {
void test() {} // 1
}
class NewName extends A {
void test() {} // 2
}
''');
createRenameRefactoringAtString('test() {} // 1');
// check status
refactoring.newName = 'NewName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Renamed method has the same name as the declaring class 'NewName'.",
expectedContextSearch: 'test() {} // 2');
}
Future<void> test_checkFinalConditions_classNameConflict_superClass() async {
await indexTestUnit('''
class NewName {
void test() {} // 1
}
class B extends NewName {
void test() {} // 2
}
''');
createRenameRefactoringAtString('test() {} // 2');
// check status
refactoring.newName = 'NewName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Renamed method has the same name as the declaring class 'NewName'.",
expectedContextSearch: 'test() {} // 1');
}
Future<void> test_checkFinalConditions_hasMember_MethodElement() async {
await indexTestUnit('''
class A {
test() {}
newName() {} // existing
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Class 'A' already declares method with name 'newName'.",
expectedContextSearch: 'newName() {} // existing');
}
Future<void> test_checkFinalConditions_OK_dropSuffix() async {
await indexTestUnit(r'''
abstract class A {
void testOld();
}
class B implements A {
void testOld() {}
}
''');
createRenameRefactoringAtString('testOld() {}');
// check status
refactoring.newName = 'test';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatusOK(status);
}
Future<void> test_checkFinalConditions_OK_noShadow() async {
await indexTestUnit('''
class A {
int newName = 0;
}
class B {
test() {}
}
class C extends A {
void f() {
print(newName);
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatusOK(status);
}
Future<void> test_checkFinalConditions_OK_noShadow_nullVisibleRange() async {
await indexTestUnit('''
class A {
int foo = 0;
A(this.foo);
}
class B {
int bar; // declaration
B(this.bar);
void referenceField() {
bar;
}
}
''');
createRenameRefactoringAtString('bar; // declaration');
// check status
refactoring.newName = 'foo';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatusOK(status);
}
Future<void>
test_checkFinalConditions_publicToPrivate_usedInNamedLibrary() async {
await indexTestUnit('''
class A {
test() {}
}
''');
await indexUnit('$testPackageLibPath/lib.dart', '''
library my.lib;
import 'test.dart';
void f(A a) {
a.test();
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = '_newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Renamed method will be invisible in '${convertPath("lib/lib.dart")}'.");
}
Future<void>
test_checkFinalConditions_publicToPrivate_usedInUnnamedLibrary() async {
await indexTestUnit('''
class A {
var foo = 1;
}
''');
await indexUnit('$testPackageLibPath/lib.dart', '''
import 'test.dart';
void f(A a) {
print(a.foo);
}
''');
createRenameRefactoringAtString('foo');
// check status
refactoring.newName = '_newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Renamed field will be invisible in '${convertPath("lib/lib.dart")}'.");
}
Future<void>
test_checkFinalConditions_shadowed_byLocalFunction_inSameClass() async {
await indexTestUnit('''
class A {
test() {}
void f() {
newName() {}
test(); // marker
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Usage of renamed method will be shadowed by function 'newName'.",
expectedContextSearch: 'test(); // marker');
}
Future<void>
test_checkFinalConditions_shadowed_byLocalVariable_inSameClass() async {
await indexTestUnit('''
class A {
test() {}
void f() {
var newName;
test(); // marker
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Usage of renamed method will be shadowed by local variable 'newName'.",
expectedContextSearch: 'test(); // marker');
}
Future<void>
test_checkFinalConditions_shadowed_byLocalVariable_inSubClass() async {
await indexTestUnit('''
class A {
test() {}
}
class B extends A {
void f() {
var newName;
test(); // marker
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Usage of renamed method will be shadowed by local variable 'newName'.",
expectedContextSearch: 'test(); // marker');
}
Future<void>
test_checkFinalConditions_shadowed_byLocalVariable_OK_qualifiedReference() async {
await indexTestUnit('''
class A {
test() {}
void f() {
var newName;
this.test(); // marker
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatusOK(status);
}
Future<void>
test_checkFinalConditions_shadowed_byLocalVariable_OK_renamedNotUsed() async {
await indexTestUnit('''
class A {
test() {}
void f() {
var newName;
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatusOK(status);
}
Future<void>
test_checkFinalConditions_shadowed_byParameter_inSameClass() async {
await indexTestUnit('''
class A {
test() {}
void f(newName) {
test(); // marker
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Usage of renamed method will be shadowed by parameter 'newName'.",
expectedContextSearch: 'test(); // marker');
}
Future<void> test_checkFinalConditions_shadowedBySub_MethodElement() async {
await indexTestUnit('''
class A {
test() {}
}
class B extends A {
newName() {} // marker
void f() {
test();
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage:
"Renamed method will be shadowed by method 'B.newName'.",
expectedContextSearch: 'newName() {} // marker');
}
Future<void> test_checkFinalConditions_shadowsSuper_FieldElement() async {
await indexTestUnit('''
class A {
int newName = 0; // marker
}
class B extends A {
test() {}
}
class C extends B {
void f() {
print(newName);
}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage: "Renamed method will shadow field 'A.newName'.",
expectedContextSearch: 'newName = 0; // marker');
}
Future<void> test_checkFinalConditions_shadowsSuper_MethodElement() async {
await indexTestUnit('''
class A {
newName() {} // marker
}
class B extends A {
test() {}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage: "Renamed method will shadow method 'A.newName'.",
expectedContextSearch: 'newName() {} // marker');
}
Future<void>
test_checkFinalConditions_shadowsSuper_MethodElement_otherLib() async {
var libCode = r'''
class A {
newName() {} // marker
}
''';
await indexUnit('$testPackageLibPath/lib.dart', libCode);
await indexTestUnit('''
import 'lib.dart';
class B extends A {
test() {}
}
''');
createRenameRefactoringAtString('test() {}');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.ERROR,
expectedMessage: "Renamed method will shadow method 'A.newName'.",
expectedContextRange: SourceRange(
libCode.indexOf('newName() {} // marker'), 'newName'.length));
}
Future<void> test_checkInitialConditions_inSDK() async {
await indexTestUnit('''
void f() {
'abc'.toUpperCase();
}
''');
createRenameRefactoringAtString('toUpperCase()');
// check status
refactoring.newName = 'NewName';
var status = await refactoring.checkInitialConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL,
expectedMessage:
"The method 'String.toUpperCase' is defined in the SDK, so cannot be renamed.");
}
Future<void> test_checkInitialConditions_operator() async {
await indexTestUnit('''
class A {
operator -(other) => this;
}
''');
createRenameRefactoringAtString('-(other)');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkInitialConditions();
assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
}
Future<void> test_checkNewName_FieldElement() async {
await indexTestUnit('''
class A {
int test = 0;
}
''');
createRenameRefactoringAtString('test = 0;');
// OK
refactoring.newName = 'newName';
assertRefactoringStatusOK(refactoring.checkNewName());
}
Future<void> test_checkNewName_MethodElement() async {
await indexTestUnit('''
class A {
test() {}
}
''');
createRenameRefactoringAtString('test() {}');
// empty
refactoring.newName = '';
assertRefactoringStatus(
refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
expectedMessage: 'Method name must not be empty.');
// same
refactoring.newName = 'test';
assertRefactoringStatus(
refactoring.checkNewName(), RefactoringProblemSeverity.FATAL,
expectedMessage:
'The new name must be different than the current name.');
// OK
refactoring.newName = 'newName';
assertRefactoringStatusOK(refactoring.checkNewName());
}
Future<void> test_createChange_FieldElement() async {
await indexTestUnit('''
class A {
int test = 0; // marker
void f() {
print(test);
test = 1;
test += 2;
}
}
class B extends A {
}
class C extends B {
get test => 1;
set test(x) {}
}
void f() {
A a = new A();
B b = new B();
C c = new C();
print(a.test);
a.test = 1;
a.test += 2;
print(b.test);
b.test = 1;
print(c.test);
c.test = 1;
}
''');
// configure refactoring
createRenameRefactoringAtString('test = 0; // marker');
expect(refactoring.refactoringName, 'Rename Field');
expect(refactoring.elementKindName, 'field');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
class A {
int newName = 0; // marker
void f() {
print(newName);
newName = 1;
newName += 2;
}
}
class B extends A {
}
class C extends B {
get newName => 1;
set newName(x) {}
}
void f() {
A a = new A();
B b = new B();
C c = new C();
print(a.newName);
a.newName = 1;
a.newName += 2;
print(b.newName);
b.newName = 1;
print(c.newName);
c.newName = 1;
}
''');
}
Future<void>
test_createChange_FieldElement_constructorFieldInitializer() async {
await indexTestUnit('''
class A {
final test;
A() : test = 5;
}
''');
// configure refactoring
createRenameRefactoringAtString('test;');
expect(refactoring.refactoringName, 'Rename Field');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
class A {
final newName;
A() : newName = 5;
}
''');
}
Future<void> test_createChange_FieldElement_fieldFormalParameter() async {
await indexTestUnit('''
class A {
final test;
A(this.test);
}
''');
// configure refactoring
createRenameRefactoringAtString('test;');
expect(refactoring.refactoringName, 'Rename Field');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
class A {
final newName;
A(this.newName);
}
''');
}
Future<void>
test_createChange_FieldElement_fieldFormalParameter_named() async {
await indexTestUnit('''
class A {
final test;
A({this.test});
}
void f() {
new A(test: 42);
}
''');
// configure refactoring
createRenameRefactoringAtString('test;');
expect(refactoring.refactoringName, 'Rename Field');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
class A {
final newName;
A({this.newName});
}
void f() {
new A(newName: 42);
}
''');
}
Future<void> test_createChange_FieldElement_invocation() async {
await indexTestUnit('''
typedef F(a);
class A {
final F test;
A(this.test);
void f() {
test(1);
}
}
void f(A a) {
a.test(2);
}
''');
// configure refactoring
createRenameRefactoringAtString('test(2);');
expect(refactoring.refactoringName, 'Rename Field');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
typedef F(a);
class A {
final F newName;
A(this.newName);
void f() {
newName(1);
}
}
void f(A a) {
a.newName(2);
}
''');
}
Future<void> test_createChange_MethodElement() async {
await indexTestUnit('''
class A {
test() {}
}
class B extends A {
test() {} // marker
}
class C extends B {
test() {}
}
class D implements A {
test() {}
}
class E {
test() {}
}
void f() {
A a = new A();
B b = new B();
C c = new C();
D d = new D();
E e = new E();
a.test();
b.test();
c.test();
d.test();
e.test();
}
''');
// configure refactoring
createRenameRefactoringAtString('test() {} // marker');
expect(refactoring.refactoringName, 'Rename Method');
expect(refactoring.elementKindName, 'method');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
class A {
newName() {}
}
class B extends A {
newName() {} // marker
}
class C extends B {
newName() {}
}
class D implements A {
newName() {}
}
class E {
test() {}
}
void f() {
A a = new A();
B b = new B();
C c = new C();
D d = new D();
E e = new E();
a.newName();
b.newName();
c.newName();
d.newName();
e.test();
}
''');
}
Future<void> test_createChange_MethodElement_potential() async {
await indexTestUnit('''
class A {
test() {}
}
void f(var a) {
a.test(); // 1
new A().test();
a.test(); // 2
}
''');
// configure refactoring
createRenameRefactoringAtString('test() {}');
expect(refactoring.refactoringName, 'Rename Method');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
await assertSuccessfulRefactoring('''
class A {
newName() {}
}
void f(var a) {
a.newName(); // 1
new A().newName();
a.newName(); // 2
}
''');
assertPotentialEdits(['test(); // 1', 'test(); // 2']);
}
Future<void> test_createChange_MethodElement_potential_inPubCache() async {
var externalPath = '$packagesRootPath/aaa/lib/lib.dart';
newFile(externalPath, content: r'''
processObj(p) {
p.test();
}
''');
writeTestPackageConfig(
config: PackageConfigFileBuilder()
..add(name: 'aaa', rootPath: '$packagesRootPath/aaa'),
);
await indexTestUnit('''
import 'package:aaa/lib.dart';
class A {
test() {}
}
void f(var a) {
a.test();
}
''');
// configure refactoring
createRenameRefactoringAtString('test() {}');
expect(refactoring.refactoringName, 'Rename Method');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
await assertSuccessfulRefactoring('''
import 'package:aaa/lib.dart';
class A {
newName() {}
}
void f(var a) {
a.newName();
}
''');
var fileEdit = refactoringChange.getFileEdit(externalPath);
expect(fileEdit, isNull);
}
Future<void>
test_createChange_MethodElement_potential_private_otherLibrary() async {
await indexUnit('/lib.dart', '''
library lib;
void f(p) {
p._test();
}
''');
await indexTestUnit('''
class A {
_test() {}
}
void f(var a) {
a._test();
new A()._test();
}
''');
// configure refactoring
createRenameRefactoringAtString('_test() {}');
expect(refactoring.refactoringName, 'Rename Method');
expect(refactoring.oldName, '_test');
refactoring.newName = 'newName';
// validate change
await assertSuccessfulRefactoring('''
class A {
newName() {}
}
void f(var a) {
a.newName();
new A().newName();
}
''');
assertNoFileChange('/lib.dart');
}
Future<void> test_createChange_outsideOfProject_declarationInPackage() async {
newFile('$workspaceRootPath/aaa/lib/aaa.dart', content: r'''
class A {
void test() {}
}
void foo(A a) {
a.test();
}
''');
writeTestPackageConfig(
config: PackageConfigFileBuilder()
..add(name: 'aaa', rootPath: '$workspaceRootPath/aaa'),
);
await indexTestUnit('''
import 'package:aaa/aaa.dart';
class B extends A {
void test() {}
}
void f(A a, B b) {
a.test();
b.test();
}
''');
createRenameRefactoringAtString('test() {}');
refactoring.newName = 'newName';
await assertSuccessfulRefactoring('''
import 'package:aaa/aaa.dart';
class B extends A {
void newName() {}
}
void f(A a, B b) {
a.newName();
b.newName();
}
''');
expect(refactoringChange.edits, hasLength(1));
expect(refactoringChange.edits[0].file, testFile);
}
Future<void> test_createChange_outsideOfProject_referenceInPart() async {
newFile('/home/part.dart', content: r'''
part of test;
void foo(A a) {
a.test();
}
''');
// To use file:// URI.
testFile = convertPath('/home/test/bin/test.dart');
await indexTestUnit('''
library test;
part '../../part.dart';
class A {
void test() {}
}
void f(A a) {
a.test();
}
''');
createRenameRefactoringAtString('test() {}');
refactoring.newName = 'newName';
await assertSuccessfulRefactoring('''
library test;
part '../../part.dart';
class A {
void newName() {}
}
void f(A a) {
a.newName();
}
''');
expect(refactoringChange.edits, hasLength(1));
expect(refactoringChange.edits[0].file, testFile);
}
Future<void> test_createChange_PropertyAccessorElement_getter() async {
await indexTestUnit('''
class A {
get test {} // marker
set test(x) {}
void f() {
print(test);
test = 1;
}
}
class B extends A {
get test {}
set test(x) {}
}
void f() {
A a = new A();
print(a.test);
a.test = 2;
B b = new B();
print(b.test);
b.test = 2;
}
''');
// configure refactoring
createRenameRefactoringAtString('test {} // marker');
expect(refactoring.refactoringName, 'Rename Field');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
class A {
get newName {} // marker
set newName(x) {}
void f() {
print(newName);
newName = 1;
}
}
class B extends A {
get newName {}
set newName(x) {}
}
void f() {
A a = new A();
print(a.newName);
a.newName = 2;
B b = new B();
print(b.newName);
b.newName = 2;
}
''');
}
Future<void> test_createChange_PropertyAccessorElement_setter() async {
await indexTestUnit('''
class A {
get test {}
set test(x) {} // marker
void f() {
print(test);
test = 1;
}
}
class B extends A {
get test {}
set test(x) {}
}
void f() {
A a = new A();
print(a.test);
a.test = 2;
B b = new B();
print(b.test);
b.test = 2;
}
''');
// configure refactoring
createRenameRefactoringAtString('test(x) {} // marker');
expect(refactoring.refactoringName, 'Rename Field');
expect(refactoring.oldName, 'test');
refactoring.newName = 'newName';
// validate change
return assertSuccessfulRefactoring('''
class A {
get newName {}
set newName(x) {} // marker
void f() {
print(newName);
newName = 1;
}
}
class B extends A {
get newName {}
set newName(x) {}
}
void f() {
A a = new A();
print(a.newName);
a.newName = 2;
B b = new B();
print(b.newName);
b.newName = 2;
}
''');
}
Future<void> test_createChange_TypeParameterElement() async {
await indexTestUnit('''
class A<Test> {
Test field;
List<Test> items = [];
A(this.field);
Test method(Test p) => field;
}
''');
// configure refactoring
createRenameRefactoringAtString('Test> {');
expect(refactoring.refactoringName, 'Rename Type Parameter');
expect(refactoring.elementKindName, 'type parameter');
expect(refactoring.oldName, 'Test');
refactoring.newName = 'NewName';
// validate change
return assertSuccessfulRefactoring('''
class A<NewName> {
NewName field;
List<NewName> items = [];
A(this.field);
NewName method(NewName p) => field;
}
''');
}
}