mirror of
https://github.com/dart-lang/sdk
synced 2024-07-20 18:05:01 +00:00
Issue 52177. Add rename refactoring for type parameters.
Bug: https://github.com/dart-lang/sdk/issues/52177 Change-Id: I4ae5bb7d72c8956af6dcd91ad28fb1d7397bf424 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/299280 Reviewed-by: Samuel Rawlins <srawlins@google.com> Reviewed-by: Phil Quitslund <pquitslund@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
f193d91ba5
commit
310f854c24
|
@ -116,6 +116,14 @@ RefactoringStatus validateTypeAliasName(String name) {
|
|||
return _validateUpperCamelCase(name, 'Type alias');
|
||||
}
|
||||
|
||||
/// Returns the [RefactoringStatus] with severity:
|
||||
/// - OK if the name is valid;
|
||||
/// - WARNING if the name is discouraged;
|
||||
/// - FATAL if the name is illegal.
|
||||
RefactoringStatus validateTypeParameter(String name) {
|
||||
return _validateUpperCamelCase(name, 'Type parameter');
|
||||
}
|
||||
|
||||
/// Returns the [RefactoringStatus] with severity:
|
||||
/// - OK if the name is valid;
|
||||
/// - WARNING if the name is discouraged;
|
||||
|
|
|
@ -19,6 +19,7 @@ import 'package:analysis_server/src/services/refactoring/legacy/rename_label.dar
|
|||
import 'package:analysis_server/src/services/refactoring/legacy/rename_library.dart';
|
||||
import 'package:analysis_server/src/services/refactoring/legacy/rename_local.dart';
|
||||
import 'package:analysis_server/src/services/refactoring/legacy/rename_parameter.dart';
|
||||
import 'package:analysis_server/src/services/refactoring/legacy/rename_type_parameter.dart';
|
||||
import 'package:analysis_server/src/services/refactoring/legacy/rename_unit_member.dart';
|
||||
import 'package:analysis_server/src/services/search/search_engine.dart';
|
||||
import 'package:analyzer/dart/analysis/results.dart';
|
||||
|
@ -441,6 +442,10 @@ abstract class RenameRefactoring implements Refactoring {
|
|||
if (element is LocalElement) {
|
||||
return RenameLocalRefactoringImpl(workspace, sessionHelper, element);
|
||||
}
|
||||
if (element is TypeParameterElement) {
|
||||
return RenameTypeParameterRefactoringImpl(
|
||||
workspace, sessionHelper, element);
|
||||
}
|
||||
if (enclosingElement is InterfaceElement) {
|
||||
return RenameClassMemberRefactoringImpl(
|
||||
workspace, sessionHelper, enclosingElement, element);
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) 2023, 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/protocol_server.dart';
|
||||
import 'package:analysis_server/src/services/correction/status.dart';
|
||||
import 'package:analysis_server/src/services/refactoring/legacy/naming_conventions.dart';
|
||||
import 'package:analysis_server/src/services/refactoring/legacy/rename.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
|
||||
class RenameTypeParameterRefactoringImpl extends RenameRefactoringImpl {
|
||||
RenameTypeParameterRefactoringImpl(
|
||||
super.workspace,
|
||||
super.sessionHelper,
|
||||
TypeParameterElement super.element,
|
||||
);
|
||||
|
||||
@override
|
||||
TypeParameterElement get element => super.element as TypeParameterElement;
|
||||
|
||||
@override
|
||||
String get refactoringName {
|
||||
return 'Rename Type Parameter';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<RefactoringStatus> checkFinalConditions() async {
|
||||
final result = RefactoringStatus();
|
||||
|
||||
final enclosing = element.enclosingElement;
|
||||
if (enclosing is TypeParameterizedElement) {
|
||||
for (final sibling in enclosing.typeParameters) {
|
||||
if (sibling.name == newName) {
|
||||
final nodeKind = sibling.kind.displayName;
|
||||
final message = "Duplicate $nodeKind '$newName'.";
|
||||
result.addError(message, newLocation_fromElement(sibling));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
RefactoringStatus checkNewName() {
|
||||
final result = super.checkNewName();
|
||||
result.addStatus(validateTypeParameter(newName));
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> fillChange() async {
|
||||
final processor =
|
||||
RenameProcessor(workspace, sessionHelper, change, newName);
|
||||
processor.addDeclarationEdit(element);
|
||||
|
||||
final references = await searchEngine.searchReferences(element);
|
||||
processor.addReferenceEdits(references);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright (c) 2023, 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/protocol_server.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'abstract_rename.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(RenameTypeParameterTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class RenameTypeParameterTest extends RenameRefactoringTest {
|
||||
Future<void> test_checkFinalConditions_duplicateName() async {
|
||||
await indexTestUnit('''
|
||||
void f<T, U>() {}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T, U');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
final status = await refactoring.checkFinalConditions();
|
||||
assertRefactoringStatus(
|
||||
status,
|
||||
RefactoringProblemSeverity.ERROR,
|
||||
expectedMessage: "Duplicate type parameter 'U'.",
|
||||
expectedContextSearch: 'U>()',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> test_checkNewName_empty() async {
|
||||
await indexTestUnit('''
|
||||
class A<T> {}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T>');
|
||||
_assertKindName();
|
||||
|
||||
refactoring.newName = '';
|
||||
assertRefactoringStatus(
|
||||
refactoring.checkNewName(),
|
||||
RefactoringProblemSeverity.FATAL,
|
||||
expectedMessage: 'Type parameter name must not be empty.',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> test_createChange_class() async {
|
||||
await indexTestUnit('''
|
||||
class A<T> {
|
||||
void foo(T a) {}
|
||||
}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T> {');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
class A<U> {
|
||||
void foo(U a) {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_class2() async {
|
||||
await indexTestUnit('''
|
||||
class A<T extends U, U, V extends List<U>> {}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('U, V');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'Z';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
class A<T extends Z, Z, V extends List<Z>> {}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_class_method() async {
|
||||
await indexTestUnit('''
|
||||
class A {
|
||||
void f<T>(T a) {}
|
||||
}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T>(');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
class A {
|
||||
void f<U>(U a) {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_enum() async {
|
||||
await indexTestUnit('''
|
||||
enum E<T> {
|
||||
v;
|
||||
void foo(T a) {}
|
||||
}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T> {');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
enum E<U> {
|
||||
v;
|
||||
void foo(U a) {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_extension() async {
|
||||
await indexTestUnit('''
|
||||
extension E<T> on int {
|
||||
void foo(T a) {}
|
||||
}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T> on');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
extension E<U> on int {
|
||||
void foo(U a) {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_functionTypeAlias() async {
|
||||
await indexTestUnit('''
|
||||
typedef void F<T>(T a);
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T>(');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
typedef void F<U>(U a);
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_functionTypeFormalParameter() async {
|
||||
await indexTestUnit('''
|
||||
void f(a<T>(T b)) {}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T>(');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
void f(a<U>(U b)) {}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_genericFunctionType() async {
|
||||
await indexTestUnit('''
|
||||
void f(void Function<T>(T a) b) {}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T>(');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
void f(void Function<U>(U a) b) {}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_localFunction() async {
|
||||
await indexTestUnit('''
|
||||
void f() {
|
||||
void g<T>(T a) {}
|
||||
}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T>(');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
void f() {
|
||||
void g<U>(U a) {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_mixin() async {
|
||||
await indexTestUnit('''
|
||||
mixin M<T> {
|
||||
void foo(T a) {}
|
||||
}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T> {');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
mixin M<U> {
|
||||
void foo(U a) {}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_topLevelFunction() async {
|
||||
await indexTestUnit('''
|
||||
void f<T>(T a) {}
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T>(');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
void f<U>(U a) {}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_createChange_typeAlias() async {
|
||||
await indexTestUnit('''
|
||||
typedef A<T> = void Function(T);
|
||||
''');
|
||||
|
||||
createRenameRefactoringAtString('T> =');
|
||||
_assertKindName();
|
||||
refactoring.newName = 'U';
|
||||
|
||||
await assertSuccessfulRefactoring('''
|
||||
typedef A<U> = void Function(U);
|
||||
''');
|
||||
}
|
||||
|
||||
void _assertKindName() {
|
||||
expect(refactoring.refactoringName, 'Rename Type Parameter');
|
||||
expect(refactoring.elementKindName, 'type parameter');
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import 'rename_import_test.dart' as rename_import_test;
|
|||
import 'rename_label_test.dart' as rename_label_test;
|
||||
import 'rename_library_test.dart' as rename_library_test;
|
||||
import 'rename_local_test.dart' as rename_local_test;
|
||||
import 'rename_type_parameter_test.dart' as rename_type_parameter;
|
||||
import 'rename_unit_member_test.dart' as rename_unit_member_test;
|
||||
|
||||
void main() {
|
||||
|
@ -40,6 +41,7 @@ void main() {
|
|||
rename_label_test.main();
|
||||
rename_library_test.main();
|
||||
rename_local_test.main();
|
||||
rename_type_parameter.main();
|
||||
rename_unit_member_test.main();
|
||||
}, name: 'legacy');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue