[DAS] Renaming positional parameters to rename hierarchy equivalent


Fixes: https://github.com/dart-lang/sdk/issues/53187
Change-Id: Ie0aaea2e24667ad04a95ce6522fd687710501230
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/412520
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Auto-Submit: Felipe Morschel <git@fmorschel.dev>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename.dart
index 6ec0203..984c842 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename.dart
@@ -8,7 +8,9 @@
 import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart';
 import 'package:analysis_server/src/services/refactoring/legacy/refactoring_internal.dart';
 import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analyzer/dart/analysis/code_style_options.dart';
 import 'package:analyzer/dart/element/element2.dart';
+import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/session_helper.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/generated/java_core.dart';
@@ -154,6 +156,11 @@
   /// Adds individual edits to [change].
   Future<void> fillChange();
 
+  CodeStyleOptions getCodeStyleOptions(File file) =>
+      sessionHelper.session.analysisContext
+          .getAnalysisOptionsForFile(file)
+          .codeStyleOptions;
+
   static String _getOldName(Element2 element) {
     if (element is ConstructorElement2) {
       var name = element.name3;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_local.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_local.dart
index 24be3f4..db8f815 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_local.dart
@@ -17,6 +17,7 @@
 import 'package:analyzer/dart/element/element2.dart';
 import 'package:analyzer/source/source_range.dart';
 import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/generated/java_core.dart';
 
 class ConflictValidatorVisitor extends RecursiveAstVisitor<void> {
   final RefactoringStatus result;
@@ -88,7 +89,12 @@
       if (_isVisibleWithTarget(declaredElement)) {
         conflictingLocals.add(declaredElement);
         var nodeKind = declaredElement.kind.displayName;
-        var message = "Duplicate $nodeKind '$newName'.";
+        var message = format(
+          "Duplicate {0} of name '{1}'{2}.",
+          nodeKind,
+          newName,
+          declaredElement.declarationLocation,
+        );
         result.addError(message, newLocation_fromElement(declaredElement));
         return;
       }
@@ -200,3 +206,26 @@
     processor.addReferenceEdits(references);
   }
 }
+
+extension on Element2 {
+  String get declarationLocation {
+    var sourceName = firstFragment.libraryFragment!.source.shortName;
+    var executable = enclosingElement2;
+    String className = '';
+    String executableName = '';
+    if (executable is MethodElement2) {
+      var namescope = executable.enclosingElement2 as ClassElement2?;
+      className = namescope?.displayName ?? '';
+      if (className.isNotEmpty && executable.displayName.isNotEmpty) {
+        className += '.';
+      }
+      executableName = executable.displayName;
+    } else if (executable is TopLevelFunctionElement) {
+      executableName = executable.displayName;
+    }
+    if (executableName.isEmpty) {
+      return " in '$sourceName'";
+    }
+    return " at $className$executableName in '$sourceName'";
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_parameter.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_parameter.dart
index 52e7454f..286befe 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_parameter.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/rename_parameter.dart
@@ -11,10 +11,12 @@
 import 'package:analysis_server/src/services/search/hierarchy.dart';
 import 'package:analyzer/dart/element/element2.dart';
 import 'package:analyzer/src/generated/java_core.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
 
 /// A [Refactoring] for renaming [FormalParameterElement]s.
 class RenameParameterRefactoringImpl extends RenameRefactoringImpl {
   List<FormalParameterElement> elements = [];
+  bool _renameAllPositionalOccurences = false;
 
   RenameParameterRefactoringImpl(
     super.workspace,
@@ -33,6 +35,7 @@
   @override
   Future<RefactoringStatus> checkFinalConditions() async {
     var result = RefactoringStatus();
+    var conflictResult = RefactoringStatus();
     await _prepareElements();
     for (var element in elements) {
       if (newName.startsWith('_') && element.isNamed) {
@@ -45,16 +48,39 @@
         break;
       }
       var resolvedUnit = await sessionHelper.getResolvedUnitByElement(element);
-      var unit = resolvedUnit?.unit;
-      unit?.accept(
-        ConflictValidatorVisitor(
-          result,
-          newName,
-          element,
-          VisibleRangesComputer.forNode(unit),
+      if (resolvedUnit != null) {
+        // If any of the resolved units have the lint enabled, we should avoid
+        // renaming method parameters separately from the other implementations.
+        if (element.isPositional && !_renameAllPositionalOccurences) {
+          _renameAllPositionalOccurences |=
+              getCodeStyleOptions(
+                resolvedUnit.file,
+              ).avoidRenamingMethodParameters;
+        }
+
+        var unit = resolvedUnit.unit;
+        unit.accept(
+          ConflictValidatorVisitor(
+            conflictResult,
+            newName,
+            element,
+            VisibleRangesComputer.forNode(unit),
+          ),
+        );
+      }
+    }
+    if (_renameAllPositionalOccurences && elements.length > 1) {
+      result.addStatus(
+        _RefactoringStatusExt.from(
+          'This will also rename all related positional parameters '
+          'to the same name.',
+          conflictResult,
         ),
       );
     }
+    if (result.problem == null) {
+      return conflictResult;
+    }
     return result;
   }
 
@@ -69,6 +95,11 @@
   Future<void> fillChange() async {
     var processor = RenameProcessor(workspace, sessionHelper, change, newName);
     for (var element in elements) {
+      if (element != this.element &&
+          element.isPositional &&
+          !_renameAllPositionalOccurences) {
+        continue;
+      }
       var fieldRenamed = false;
       if (element is FieldFormalParameterElement2) {
         var field = element.field2;
@@ -104,10 +135,31 @@
   Future<void> _prepareElements() async {
     var element = this.element;
     if (element.isNamed) {
-      elements =
-          (await getHierarchyNamedParameters(searchEngine, element)).toList();
-    } else {
-      elements = [element];
+      elements = await getHierarchyNamedParameters(searchEngine, element);
+    } else if (element.isPositional) {
+      elements = await getHierarchyPositionalParameters(searchEngine, element);
     }
   }
 }
+
+extension _RefactoringStatusExt on RefactoringStatus {
+  String get messagesAggregated {
+    if (problems.isEmpty) {
+      return '';
+    }
+    if (problems.length == 1) {
+      return '\n${problems.first.message}';
+    }
+    return '\n${problems.first.message} And ${problems.length - 1} more '
+        'error${problems.length > 1 ? 's' : ''}.';
+  }
+
+  static RefactoringStatus from(String message, RefactoringStatus result) {
+    var constructor = switch (result.severity) {
+      RefactoringProblemSeverity.ERROR => RefactoringStatus.error,
+      RefactoringProblemSeverity.FATAL => RefactoringStatus.fatal,
+      _ => RefactoringStatus.warning,
+    };
+    return constructor(message + result.messagesAggregated);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/search/hierarchy.dart b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
index 332129d..00d01a4 100644
--- a/pkg/analysis_server/lib/src/services/search/hierarchy.dart
+++ b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
@@ -221,6 +221,37 @@
   return [element];
 }
 
+Future<List<FormalParameterElement>> getHierarchyPositionalParameters(
+  SearchEngine searchEngine,
+  FormalParameterElement element,
+) async {
+  if (element.isPositional) {
+    var method = element.enclosingElement2;
+    if (method is MethodElement2) {
+      var index = method.parameterIndex(element);
+      // Should not ever happen but this means we can't find the index.
+      if (index == null) {
+        return [element];
+      }
+      var hierarchyParameters = <FormalParameterElement>[];
+      var hierarchyMembers = await getHierarchyMembers(searchEngine, method);
+      for (var hierarchyMethod in hierarchyMembers) {
+        if (hierarchyMethod is MethodElement2) {
+          for (var hierarchyParameter in hierarchyMethod.formalParameters) {
+            if (hierarchyParameter.isPositional &&
+                hierarchyMethod.parameterIndex(hierarchyParameter) == index) {
+              hierarchyParameters.add(hierarchyParameter);
+              break;
+            }
+          }
+        }
+      }
+      return hierarchyParameters;
+    }
+  }
+  return [element];
+}
+
 /// Returns non-synthetic members of the given [InterfaceElement2] and its super
 /// classes.
 ///
@@ -254,3 +285,18 @@
   }
   return element.name3;
 }
+
+extension on MethodElement2 {
+  int? parameterIndex(FormalParameterElement parameter) {
+    var index = 0;
+    for (var positionalParameter in formalParameters.where(
+      (p) => p.isPositional,
+    )) {
+      if (positionalParameter == parameter) {
+        return index;
+      }
+      index++;
+    }
+    return null;
+  }
+}
diff --git a/pkg/analysis_server/test/edit/refactoring_test.dart b/pkg/analysis_server/test/edit/refactoring_test.dart
index 7b85a6e..8a95dec 100644
--- a/pkg/analysis_server/test/edit/refactoring_test.dart
+++ b/pkg/analysis_server/test/edit/refactoring_test.dart
@@ -2739,7 +2739,7 @@
       expect(problems, hasLength(1));
       assertResultProblemsError(
         problems,
-        "Duplicate local variable 'newName'.",
+        "Duplicate local variable of name 'newName' at f in 'test.dart'.",
       );
     });
   }
diff --git a/pkg/analysis_server/test/services/refactoring/legacy/rename_local_test.dart b/pkg/analysis_server/test/services/refactoring/legacy/rename_local_test.dart
index 1955f62..1462246 100644
--- a/pkg/analysis_server/test/services/refactoring/legacy/rename_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/legacy/rename_local_test.dart
@@ -30,7 +30,7 @@
     assertRefactoringStatus(
       status,
       RefactoringProblemSeverity.ERROR,
-      expectedMessage: "Duplicate function 'newName'.",
+      expectedMessage: "Duplicate function of name 'newName' in 'test.dart'.",
       expectedContextSearch: 'newName() => 1',
     );
   }
@@ -49,7 +49,7 @@
     assertRefactoringStatus(
       status,
       RefactoringProblemSeverity.ERROR,
-      expectedMessage: "Duplicate function 'newName'.",
+      expectedMessage: "Duplicate function of name 'newName' in 'test.dart'.",
     );
   }
 
@@ -69,7 +69,9 @@
     assertRefactoringStatus(
       status,
       RefactoringProblemSeverity.ERROR,
-      expectedMessage: "Duplicate local variable 'newName'.",
+      expectedMessage:
+          "Duplicate local variable of name 'newName' at f in "
+          "'test.dart'.",
       expectedContextSearch: 'newName = 1;',
     );
   }
@@ -88,7 +90,9 @@
     assertRefactoringStatus(
       status,
       RefactoringProblemSeverity.ERROR,
-      expectedMessage: "Duplicate local variable 'newName'.",
+      expectedMessage:
+          "Duplicate local variable of name 'newName' at f in "
+          "'test.dart'.",
       expectedContextSearch: 'newName = 1;',
     );
   }
@@ -178,34 +182,6 @@
   }
 
   Future<void>
-  test_checkFinalConditions_shadows_classMember_namedParameter() async {
-    await indexTestUnit('''
-class A {
-  foo({test = 1}) { // in A
-  }
-}
-class B extends A {
-  var newName = 1;
-  foo({test = 1}) {
-    print(newName);
-  }
-}
-''');
-    createRenameRefactoringAtString('test = 1}) { // in A');
-    // check status
-    refactoring.newName = 'newName';
-    var status = await refactoring.checkFinalConditions();
-    assertRefactoringStatus(
-      status,
-      RefactoringProblemSeverity.ERROR,
-      expectedMessage:
-          'Usage of field "B.newName" declared in "test.dart" '
-          'will be shadowed by renamed parameter.',
-      expectedContextSearch: 'newName);',
-    );
-  }
-
-  Future<void>
   test_checkFinalConditions_shadows_classMemberOK_qualifiedReference() async {
     await indexTestUnit('''
 class A {
@@ -294,47 +270,6 @@
     assertRefactoringStatusOK(refactoring.checkNewName());
   }
 
-  Future<void> test_checkNewName_ParameterElement() async {
-    await indexTestUnit('''
-void f(test) {
-}
-''');
-    createRenameRefactoringAtString('test) {');
-    // empty
-    refactoring.newName = '';
-    assertRefactoringStatus(
-      refactoring.checkNewName(),
-      RefactoringProblemSeverity.FATAL,
-      expectedMessage: 'Parameter name must not be empty.',
-    );
-    // OK
-    refactoring.newName = 'newName';
-    assertRefactoringStatusOK(refactoring.checkNewName());
-  }
-
-  Future<void> test_createChange_closure_parameter() async {
-    await indexTestUnit('''
-void f(void Function(int) _) {}
-
-void g() => f((parameter) {
-  print(parameter);
-});
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('parameter) {');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    return assertSuccessfulRefactoring('''
-void f(void Function(int) _) {}
-
-void g() => f((newName) {
-  print(newName);
-});
-''');
-  }
-
   Future<void> test_createChange_localFunction() async {
     await indexTestUnit('''
 void f() {
@@ -539,299 +474,6 @@
 ''');
   }
 
-  Future<void> test_createChange_parameter_named() async {
-    await indexTestUnit('''
-myFunction({required int test}) {
-  test = 1;
-  test += 2;
-  print(test);
-}
-void f() {
-  myFunction(test: 2);
-}
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('test}) {');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    return assertSuccessfulRefactoring('''
-myFunction({required int newName}) {
-  newName = 1;
-  newName += 2;
-  print(newName);
-}
-void f() {
-  myFunction(newName: 2);
-}
-''');
-  }
-
-  Future<void> test_createChange_parameter_named_anywhere() async {
-    await indexTestUnit('''
-myFunction(int a, int b, {required int test}) {
-  test = 1;
-  test += 2;
-  print(test);
-}
-void f() {
-  myFunction(0, test: 2, 1);
-}
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('test}) {');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    return assertSuccessfulRefactoring('''
-myFunction(int a, int b, {required int newName}) {
-  newName = 1;
-  newName += 2;
-  print(newName);
-}
-void f() {
-  myFunction(0, newName: 2, 1);
-}
-''');
-  }
-
-  Future<void> test_createChange_parameter_named_inOtherFile() async {
-    var a = convertPath('$testPackageLibPath/a.dart');
-    var b = convertPath('$testPackageLibPath/b.dart');
-
-    newFile(a, r'''
-class A {
-  A({test});
-}
-''');
-    newFile(b, r'''
-import 'a.dart';
-
-void f() {
-  new A(test: 2);
-}
-''');
-    await analyzeTestPackageFiles();
-
-    testFilePath = a;
-    await resolveTestFile();
-
-    createRenameRefactoringAtString('test});');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    refactoring.newName = 'newName';
-
-    await assertSuccessfulRefactoring('''
-class A {
-  A({newName});
-}
-''');
-    assertFileChangeResult(b, '''
-import 'a.dart';
-
-void f() {
-  new A(newName: 2);
-}
-''');
-  }
-
-  Future<void>
-  test_createChange_parameter_named_ofConstructor_genericClass() async {
-    await indexTestUnit('''
-class A<T> {
-  A({required T test});
-}
-
-void f() {
-  A(test: 0);
-}
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('test}');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    return assertSuccessfulRefactoring('''
-class A<T> {
-  A({required T newName});
-}
-
-void f() {
-  A(newName: 0);
-}
-''');
-  }
-
-  Future<void> test_createChange_parameter_named_ofMethod_genericClass() async {
-    await indexTestUnit('''
-class A<T> {
-  void foo({required T test}) {}
-}
-
-void f(A<int> a) {
-  a.foo(test: 0);
-}
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('test}');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    return assertSuccessfulRefactoring('''
-class A<T> {
-  void foo({required T newName}) {}
-}
-
-void f(A<int> a) {
-  a.foo(newName: 0);
-}
-''');
-  }
-
-  Future<void> test_createChange_parameter_named_super() async {
-    await indexTestUnit('''
-class A {
-  A({required int test}); // 0
-}
-class B extends A {
-  B({required super.test});
-}
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('test}); // 0');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    return assertSuccessfulRefactoring('''
-class A {
-  A({required int newName}); // 0
-}
-class B extends A {
-  B({required super.newName});
-}
-''');
-  }
-
-  Future<void> test_createChange_parameter_named_updateHierarchy() async {
-    await indexUnit('$testPackageLibPath/test2.dart', '''
-library test2;
-class A {
-  void foo({int? test}) {
-    print(test);
-  }
-}
-class B extends A {
-  void foo({int? test}) {
-    print(test);
-  }
-}
-''');
-    await indexTestUnit('''
-import 'test2.dart';
-void f() {
-  new A().foo(test: 10);
-  new B().foo(test: 20);
-  new C().foo(test: 30);
-}
-class C extends A {
-  void foo({int? test}) {
-    print(test);
-  }
-}
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('test: 20');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    await assertSuccessfulRefactoring('''
-import 'test2.dart';
-void f() {
-  new A().foo(newName: 10);
-  new B().foo(newName: 20);
-  new C().foo(newName: 30);
-}
-class C extends A {
-  void foo({int? newName}) {
-    print(newName);
-  }
-}
-''');
-    assertFileChangeResult('$testPackageLibPath/test2.dart', '''
-library test2;
-class A {
-  void foo({int? newName}) {
-    print(newName);
-  }
-}
-class B extends A {
-  void foo({int? newName}) {
-    print(newName);
-  }
-}
-''');
-  }
-
-  Future<void> test_createChange_parameter_optionalPositional() async {
-    await indexTestUnit('''
-myFunction([int? test]) {
-  test = 1;
-  test += 2;
-  print(test);
-}
-void f() {
-  myFunction(2);
-}
-''');
-    // configure refactoring
-    createRenameRefactoringAtString('test]) {');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-    // validate change
-    return assertSuccessfulRefactoring('''
-myFunction([int? newName]) {
-  newName = 1;
-  newName += 2;
-  print(newName);
-}
-void f() {
-  myFunction(2);
-}
-''');
-  }
-
-  Future<void> test_createChange_parameter_positional_super() async {
-    await indexTestUnit('''
-class A {
-  A(int test); // 0
-}
-class B extends A {
-  B(super.test);
-}
-''');
-
-    createRenameRefactoringAtString('test); // 0');
-    expect(refactoring.refactoringName, 'Rename Parameter');
-    expect(refactoring.elementKindName, 'parameter');
-    refactoring.newName = 'newName';
-
-    // The name of the super-formal parameter does not have to be the same.
-    // So, we don't rename it.
-    return assertSuccessfulRefactoring('''
-class A {
-  A(int newName); // 0
-}
-class B extends A {
-  B(super.test);
-}
-''');
-  }
-
   Future<void> test_createChange_patternVariable_declarationStatement() async {
     await indexTestUnit('''
 void f(Object? x) {
diff --git a/pkg/analysis_server/test/services/refactoring/legacy/rename_parameter_test.dart b/pkg/analysis_server/test/services/refactoring/legacy/rename_parameter_test.dart
new file mode 100644
index 0000000..29018ab
--- /dev/null
+++ b/pkg/analysis_server/test/services/refactoring/legacy/rename_parameter_test.dart
@@ -0,0 +1,631 @@
+// Copyright (c) 2025, 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_plugin/protocol/protocol_common.dart';
+import 'package:linter/src/lint_names.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'abstract_rename.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(RenameNamedParameterTest);
+    defineReflectiveTests(RenamePositionalParameterTest);
+  });
+}
+
+@reflectiveTest
+class RenameNamedParameterTest extends RenameRefactoringTest {
+  Future<void> test_checkFinalConditions_shadows_classMember() async {
+    await indexTestUnit('''
+class A {
+  foo({test = 1}) { // in A
+  }
+}
+class B extends A {
+  var newName = 1;
+  foo({test = 1}) {
+    print(newName);
+  }
+}
+''');
+    createRenameRefactoringAtString('test = 1}) { // in A');
+    // check status
+    refactoring.newName = 'newName';
+    var status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+      status,
+      RefactoringProblemSeverity.ERROR,
+      expectedMessage:
+          'Usage of field "B.newName" declared in "test.dart" '
+          'will be shadowed by renamed parameter.',
+      expectedContextSearch: 'newName);',
+    );
+  }
+
+  Future<void> test_createChange() async {
+    await indexTestUnit('''
+myFunction({required int test}) {
+  test = 1;
+  test += 2;
+  print(test);
+}
+void f() {
+  myFunction(test: 2);
+}
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('test}) {');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    return assertSuccessfulRefactoring('''
+myFunction({required int newName}) {
+  newName = 1;
+  newName += 2;
+  print(newName);
+}
+void f() {
+  myFunction(newName: 2);
+}
+''');
+  }
+
+  Future<void> test_createChange_anywhere() async {
+    await indexTestUnit('''
+myFunction(int a, int b, {required int test}) {
+  test = 1;
+  test += 2;
+  print(test);
+}
+void f() {
+  myFunction(0, test: 2, 1);
+}
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('test}) {');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    return assertSuccessfulRefactoring('''
+myFunction(int a, int b, {required int newName}) {
+  newName = 1;
+  newName += 2;
+  print(newName);
+}
+void f() {
+  myFunction(0, newName: 2, 1);
+}
+''');
+  }
+
+  Future<void> test_createChange_inOtherFile() async {
+    var a = convertPath('$testPackageLibPath/a.dart');
+    var b = convertPath('$testPackageLibPath/b.dart');
+
+    newFile(a, r'''
+class A {
+  A({test});
+}
+''');
+    newFile(b, r'''
+import 'a.dart';
+
+void f() {
+  new A(test: 2);
+}
+''');
+    await analyzeTestPackageFiles();
+
+    testFilePath = a;
+    await resolveTestFile();
+
+    createRenameRefactoringAtString('test});');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    refactoring.newName = 'newName';
+
+    await assertSuccessfulRefactoring('''
+class A {
+  A({newName});
+}
+''');
+    assertFileChangeResult(b, '''
+import 'a.dart';
+
+void f() {
+  new A(newName: 2);
+}
+''');
+  }
+
+  Future<void> test_createChange_ofConstructor_genericClass() async {
+    await indexTestUnit('''
+class A<T> {
+  A({required T test});
+}
+
+void f() {
+  A(test: 0);
+}
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('test}');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    return assertSuccessfulRefactoring('''
+class A<T> {
+  A({required T newName});
+}
+
+void f() {
+  A(newName: 0);
+}
+''');
+  }
+
+  Future<void> test_createChange_ofMethod_genericClass() async {
+    await indexTestUnit('''
+class A<T> {
+  void foo({required T test}) {}
+}
+
+void f(A<int> a) {
+  a.foo(test: 0);
+}
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('test}');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    return assertSuccessfulRefactoring('''
+class A<T> {
+  void foo({required T newName}) {}
+}
+
+void f(A<int> a) {
+  a.foo(newName: 0);
+}
+''');
+  }
+
+  Future<void> test_createChange_super() async {
+    await indexTestUnit('''
+class A {
+  A({required int test}); // 0
+}
+class B extends A {
+  B({required super.test});
+}
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('test}); // 0');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    return assertSuccessfulRefactoring('''
+class A {
+  A({required int newName}); // 0
+}
+class B extends A {
+  B({required super.newName});
+}
+''');
+  }
+
+  Future<void> test_createChange_updateHierarchy() async {
+    await indexUnit('$testPackageLibPath/test2.dart', '''
+library test2;
+class A {
+  void foo({int? test}) {
+    print(test);
+  }
+}
+class B extends A {
+  void foo({int? test}) {
+    print(test);
+  }
+}
+''');
+    await indexTestUnit('''
+import 'test2.dart';
+void f() {
+  new A().foo(test: 10);
+  new B().foo(test: 20);
+  new C().foo(test: 30);
+}
+class C extends A {
+  void foo({int? test}) {
+    print(test);
+  }
+}
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('test: 20');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    await assertSuccessfulRefactoring('''
+import 'test2.dart';
+void f() {
+  new A().foo(newName: 10);
+  new B().foo(newName: 20);
+  new C().foo(newName: 30);
+}
+class C extends A {
+  void foo({int? newName}) {
+    print(newName);
+  }
+}
+''');
+    assertFileChangeResult('$testPackageLibPath/test2.dart', '''
+library test2;
+class A {
+  void foo({int? newName}) {
+    print(newName);
+  }
+}
+class B extends A {
+  void foo({int? newName}) {
+    print(newName);
+  }
+}
+''');
+  }
+}
+
+@reflectiveTest
+class RenamePositionalParameterTest extends RenameRefactoringTest {
+  Future<void> test_checkNewName() async {
+    await indexTestUnit('''
+void f(test) {
+}
+''');
+    createRenameRefactoringAtString('test) {');
+    // empty
+    refactoring.newName = '';
+    assertRefactoringStatus(
+      refactoring.checkNewName(),
+      RefactoringProblemSeverity.FATAL,
+      expectedMessage: 'Parameter name must not be empty.',
+    );
+    // OK
+    refactoring.newName = 'newName';
+    assertRefactoringStatusOK(refactoring.checkNewName());
+  }
+
+  Future<void> test_createChange_closure() async {
+    await indexTestUnit('''
+void f(void Function(int) _) {}
+
+void g() => f((parameter) {
+  print(parameter);
+});
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('parameter) {');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    return assertSuccessfulRefactoring('''
+void f(void Function(int) _) {}
+
+void g() => f((newName) {
+  print(newName);
+});
+''');
+  }
+
+  Future<void> test_createChange_optional() async {
+    await indexTestUnit('''
+myFunction([int? test]) {
+  test = 1;
+  test += 2;
+  print(test);
+}
+void f() {
+  myFunction(2);
+}
+''');
+    // configure refactoring
+    createRenameRefactoringAtString('test]) {');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+    // validate change
+    return assertSuccessfulRefactoring('''
+myFunction([int? newName]) {
+  newName = 1;
+  newName += 2;
+  print(newName);
+}
+void f() {
+  myFunction(2);
+}
+''');
+  }
+
+  Future<void> test_createChange_super() async {
+    await indexTestUnit('''
+class A {
+  A(int test); // 0
+}
+class B extends A {
+  B(super.test);
+}
+''');
+
+    createRenameRefactoringAtString('test); // 0');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    refactoring.newName = 'newName';
+
+    // The name of the super-formal parameter does not have to be the same.
+    // So, we don't rename it.
+    return assertSuccessfulRefactoring('''
+class A {
+  A(int newName); // 0
+}
+class B extends A {
+  B(super.test);
+}
+''');
+  }
+
+  Future<void> test_function_shadow() async {
+    await indexTestUnit('''
+void function(int a) {
+  int b = 0;
+}
+''');
+    createRenameRefactoringAtString('a) {');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    var status = await refactoring.checkFinalConditions();
+    return assertRefactoringStatus(
+      status,
+      RefactoringProblemSeverity.ERROR,
+      expectedMessage:
+          "Duplicate local variable of name 'b' at function in "
+          "'test.dart'.",
+    );
+  }
+
+  Future<void> test_hierarchy_override_lint_shadow_double() async {
+    createAnalysisOptionsFile(
+      lints: [LintNames.avoid_renaming_method_parameters],
+    );
+    await indexTestUnit('''
+class C {
+  void m(int? a) {
+    int b = 0;
+  }
+}
+
+class D extends C {
+  @override
+  void m(int? a) {} // marker
+}
+
+class E extends C {
+  @override
+  void m(int? a) {
+    int b = 0;
+  }
+}
+''');
+    createRenameRefactoringAtString('a) {} // marker');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    var status = await refactoring.checkFinalConditions();
+    return assertRefactoringStatus(
+      status,
+      RefactoringProblemSeverity.ERROR,
+      expectedMessage:
+          'This will also rename all related positional parameters to the same '
+          "name.\nDuplicate local variable of name 'b' at E.m in 'test.dart'. "
+          'And 1 more errors.',
+    );
+  }
+
+  Future<void> test_subclass_override() async {
+    await indexTestUnit('''
+class C {
+  void m(int? a) {}
+}
+class D extends C {
+  @override
+  void m(int? a) {} // marker
+}
+''');
+    createRenameRefactoringAtString('a) {} // marker');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    return assertSuccessfulRefactoring('''
+class C {
+  void m(int? a) {}
+}
+class D extends C {
+  @override
+  void m(int? b) {} // marker
+}
+''');
+  }
+
+  Future<void> test_subclass_override_lint() async {
+    createAnalysisOptionsFile(
+      lints: [LintNames.avoid_renaming_method_parameters],
+    );
+    await indexTestUnit('''
+class C {
+  void m(int? a) {}
+}
+class D extends C {
+  @override
+  void m(int? a) {} // marker
+}
+''');
+    createRenameRefactoringAtString('a) {} // marker');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    var status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+      status,
+      RefactoringProblemSeverity.WARNING,
+      expectedMessage:
+          'This will also rename all related positional '
+          'parameters to the same name.',
+    );
+    refactoringChange = await refactoring.createChange();
+    assertTestChangeResult('''
+class C {
+  void m(int? b) {}
+}
+class D extends C {
+  @override
+  void m(int? b) {} // marker
+}
+''');
+  }
+
+  Future<void> test_subclass_override_lint_shadow() async {
+    createAnalysisOptionsFile(
+      lints: [LintNames.avoid_renaming_method_parameters],
+    );
+    await indexTestUnit('''
+class C {
+  void m(int? a) {
+    int b = 0;
+  }
+}
+class D extends C {
+  @override
+  void m(int? a) {} // marker
+}
+''');
+    createRenameRefactoringAtString('a) {} // marker');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    var status = await refactoring.checkFinalConditions();
+    return assertRefactoringStatus(
+      status,
+      RefactoringProblemSeverity.ERROR,
+      expectedMessage:
+          'This will also rename all related positional parameters to the same '
+          "name.\nDuplicate local variable of name 'b' at C.m in 'test.dart'.",
+    );
+  }
+
+  Future<void> test_superclass_override() async {
+    await indexTestUnit('''
+class C {
+  void m(int? a) {} // marker
+}
+class D extends C {
+  @override
+  void m(int? a) {}
+}
+''');
+    createRenameRefactoringAtString('a) {} // marker');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    return assertSuccessfulRefactoring('''
+class C {
+  void m(int? b) {} // marker
+}
+class D extends C {
+  @override
+  void m(int? a) {}
+}
+''');
+  }
+
+  Future<void> test_superclass_override_lint() async {
+    createAnalysisOptionsFile(
+      lints: [LintNames.avoid_renaming_method_parameters],
+    );
+    await indexTestUnit('''
+class C {
+  void m(int? a) {} // marker
+}
+class D extends C {
+  @override
+  void m(int? a) {}
+}
+''');
+    createRenameRefactoringAtString('a) {} // marker');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    var status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+      status,
+      RefactoringProblemSeverity.WARNING,
+      expectedMessage:
+          'This will also rename all related positional parameters to the same '
+          'name.',
+    );
+    refactoringChange = await refactoring.createChange();
+    assertTestChangeResult('''
+class C {
+  void m(int? b) {} // marker
+}
+class D extends C {
+  @override
+  void m(int? b) {}
+}
+''');
+  }
+
+  Future<void> test_superclass_override_lint_shadow() async {
+    createAnalysisOptionsFile(
+      lints: [LintNames.avoid_renaming_method_parameters],
+    );
+    await indexTestUnit('''
+class C {
+  void m(int? a) {} // marker
+}
+class D extends C {
+  @override
+  void m(int? a) {
+    int b = 0;
+  }
+}
+''');
+    createRenameRefactoringAtString('a) {} // marker');
+    expect(refactoring.refactoringName, 'Rename Parameter');
+    expect(refactoring.elementKindName, 'parameter');
+    expect(refactoring.oldName, 'a');
+    refactoring.newName = 'b';
+    var status = await refactoring.checkFinalConditions();
+    return assertRefactoringStatus(
+      status,
+      RefactoringProblemSeverity.ERROR,
+      expectedMessage:
+          'This will also rename all related positional parameters to the same '
+          "name.\nDuplicate local variable of name 'b' at D.m in 'test.dart'.",
+    );
+  }
+}
diff --git a/pkg/analysis_server/test/services/refactoring/legacy/test_all.dart b/pkg/analysis_server/test/services/refactoring/legacy/test_all.dart
index 4b8d30f..8d3e190 100644
--- a/pkg/analysis_server/test/services/refactoring/legacy/test_all.dart
+++ b/pkg/analysis_server/test/services/refactoring/legacy/test_all.dart
@@ -20,6 +20,7 @@
 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_parameter_test.dart' as rename_parameter;
 import 'rename_type_parameter_test.dart' as rename_type_parameter;
 import 'rename_unit_member_test.dart' as rename_unit_member_test;
 
@@ -41,6 +42,7 @@
     rename_label_test.main();
     rename_library_test.main();
     rename_local_test.main();
+    rename_parameter.main();
     rename_type_parameter.main();
     rename_unit_member_test.main();
   }, name: 'legacy');
diff --git a/pkg/analyzer/api.txt b/pkg/analyzer/api.txt
index 73acf72..2c702c5 100644
--- a/pkg/analyzer/api.txt
+++ b/pkg/analyzer/api.txt
@@ -54,6 +54,7 @@
   CodeStyleOptions (class extends Object):
     new (constructor: CodeStyleOptions Function())
     addTrailingCommas (getter: bool)
+    avoidRenamingMethodParameters (getter: bool)
     finalInForEach (getter: bool)
     makeLocalsFinal (getter: bool)
     preferConstDeclarations (getter: bool)
diff --git a/pkg/analyzer/lib/dart/analysis/code_style_options.dart b/pkg/analyzer/lib/dart/analysis/code_style_options.dart
index bae0808..cc500e8 100644
--- a/pkg/analyzer/lib/dart/analysis/code_style_options.dart
+++ b/pkg/analyzer/lib/dart/analysis/code_style_options.dart
@@ -13,6 +13,11 @@
   /// should be inserted in function calls and declarations.
   bool get addTrailingCommas;
 
+  /// Whether the `avoid_renaming_method_parameters` is enabled and method
+  /// parameters should not be renamed separately from the other
+  /// implementations.
+  bool get avoidRenamingMethodParameters;
+
   /// Whether local variables should be `final` inside a for-loop.
   bool get finalInForEach;
 
diff --git a/pkg/analyzer/lib/src/analysis_options/code_style_options.dart b/pkg/analyzer/lib/src/analysis_options/code_style_options.dart
index 95feced..adfdebc 100644
--- a/pkg/analyzer/lib/src/analysis_options/code_style_options.dart
+++ b/pkg/analyzer/lib/src/analysis_options/code_style_options.dart
@@ -20,6 +20,10 @@
   bool get addTrailingCommas => _isLintEnabled('require_trailing_commas');
 
   @override
+  bool get avoidRenamingMethodParameters =>
+      _isLintEnabled('avoid_renaming_method_parameters');
+
+  @override
   bool get finalInForEach => _isLintEnabled('prefer_final_in_for_each');
 
   @override