Fix for searching references to named parameters in hierarchy.

R=brianwilkerson@google.com

Change-Id: I932eae1a1c6e440fab2034bffc7ec850f1216b9c
Reviewed-on: https://dart-review.googlesource.com/55841
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/search/element_references.dart b/pkg/analysis_server/lib/src/search/element_references.dart
index 4b53584..fc60fb9 100644
--- a/pkg/analysis_server/lib/src/search/element_references.dart
+++ b/pkg/analysis_server/lib/src/search/element_references.dart
@@ -68,12 +68,15 @@
   /**
    * Returns a [Future] completing with [Element]s to search references to.
    *
-   * If a [ClassMemberElement] is given, each corresponding [Element] in the
-   * hierarchy is returned.
+   * If a [ClassMemberElement] or a named [ParameterElement] is given, each
+   * corresponding [Element] in the hierarchy is returned.
    *
    * Otherwise, only references to [element] should be searched.
    */
   Future<Iterable<Element>> _getRefElements(Element element) {
+    if (element is ParameterElement && element.isNamed) {
+      return getHierarchyNamedParameters(searchEngine, element);
+    }
     if (element is ClassMemberElement) {
       return getHierarchyMembers(searchEngine, element);
     }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
index 4b1c9eb4..eca6220 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_local.dart
@@ -26,7 +26,7 @@
   final AstProvider astProvider;
   final ResolvedUnitCache unitCache;
 
-  Set<LocalElement> elements = new Set<LocalElement>();
+  List<LocalElement> elements = [];
 
   RenameLocalRefactoringImpl(
       SearchEngine searchEngine, this.astProvider, LocalElement element)
@@ -92,25 +92,11 @@
    * Fills [elements] with [Element]s to rename.
    */
   Future _prepareElements() async {
-    Element enclosing = element.enclosingElement;
-    if (enclosing is MethodElement &&
-        element is ParameterElement &&
-        (element as ParameterElement).isNamed) {
-      // prepare hierarchy methods
-      Set<ClassMemberElement> methods =
-          await getHierarchyMembers(searchEngine, enclosing);
-      // add named parameter from each method
-      for (ClassMemberElement method in methods) {
-        if (method is MethodElement) {
-          for (ParameterElement parameter in method.parameters) {
-            if (parameter.isNamed && parameter.name == element.name) {
-              elements.add(parameter);
-            }
-          }
-        }
-      }
+    Element element = this.element;
+    if (element is ParameterElement && element.isNamed) {
+      elements = await getHierarchyNamedParameters(searchEngine, element);
     } else {
-      elements = new Set.from([element]);
+      elements = [element];
     }
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/search/hierarchy.dart b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
index 3f9bb483..6441488 100644
--- a/pkg/analysis_server/lib/src/services/search/hierarchy.dart
+++ b/pkg/analysis_server/lib/src/services/search/hierarchy.dart
@@ -100,6 +100,34 @@
 }
 
 /**
+ * If the [element] is a named parameter in a [MethodElement], return all
+ * corresponding named parameters in the method hierarchy.
+ */
+Future<List<ParameterElement>> getHierarchyNamedParameters(
+    SearchEngine searchEngine, ParameterElement element) async {
+  if (element.isNamed) {
+    Element method = element.enclosingElement;
+    if (method is MethodElement) {
+      var hierarchyParameters = <ParameterElement>[];
+      var hierarchyMembers = await getHierarchyMembers(searchEngine, method);
+      for (ClassMemberElement hierarchyMethod in hierarchyMembers) {
+        if (hierarchyMethod is MethodElement) {
+          for (var hierarchyParameter in hierarchyMethod.parameters) {
+            if (hierarchyParameter.isNamed &&
+                hierarchyParameter.name == element.name) {
+              hierarchyParameters.add(hierarchyParameter);
+              break;
+            }
+          }
+        }
+      }
+      return hierarchyParameters;
+    }
+  }
+  return [element];
+}
+
+/**
  * Returns non-synthetic members of the given [ClassElement] and its super
  * classes.
  *
diff --git a/pkg/analysis_server/test/search/element_references_test.dart b/pkg/analysis_server/test/search/element_references_test.dart
index 3e411f4..d320dbb 100644
--- a/pkg/analysis_server/test/search/element_references_test.dart
+++ b/pkg/analysis_server/test/search/element_references_test.dart
@@ -305,6 +305,30 @@
     assertHasResult(SearchResultKind.INVOCATION, 'mmm(20)');
   }
 
+  test_hierarchy_namedParameter() async {
+    addTestFile('''
+class A {
+  m({p}) {} // in A
+}
+class B extends A {
+  m({p}) {} // in B
+}
+class C extends B {
+  m({p}) {} // in C
+}
+main(A a, B b, C c) {
+  a.m(p: 1);
+  b.m(p: 2);
+  c.m(p: 3);
+}
+''');
+    await findElementReferences('p}) {} // in B', false);
+    expect(searchElement.kind, ElementKind.PARAMETER);
+    assertHasResult(SearchResultKind.REFERENCE, 'p: 1');
+    assertHasResult(SearchResultKind.REFERENCE, 'p: 2');
+    assertHasResult(SearchResultKind.REFERENCE, 'p: 3');
+  }
+
   test_label() async {
     addTestFile('''
 main() {
diff --git a/pkg/analysis_server/test/services/search/hierarchy_test.dart b/pkg/analysis_server/test/services/search/hierarchy_test.dart
index d81893d..a6827ee 100644
--- a/pkg/analysis_server/test/services/search/hierarchy_test.dart
+++ b/pkg/analysis_server/test/services/search/hierarchy_test.dart
@@ -254,6 +254,95 @@
     return Future.wait([futureA, futureB, futureD]);
   }
 
+  test_getHierarchyNamedParameters() async {
+    await _indexTestUnit('''
+class A {
+  foo({p}) {}
+}
+class B extends A {
+  foo({p}) {}
+}
+class C extends B {
+  foo({p}) {}
+}
+class D {
+  foo({p}) {}
+}
+class E extends D {
+  foo({p}) {}
+}
+''');
+    ClassElement classA = findElement('A');
+    ClassElement classB = findElement('B');
+    ClassElement classC = findElement('C');
+    ClassElement classD = findElement('D');
+    ClassElement classE = findElement('E');
+    ParameterElement parameterA = classA.methods[0].parameters[0];
+    ParameterElement parameterB = classB.methods[0].parameters[0];
+    ParameterElement parameterC = classC.methods[0].parameters[0];
+    ParameterElement parameterD = classD.methods[0].parameters[0];
+    ParameterElement parameterE = classE.methods[0].parameters[0];
+
+    {
+      var result = await getHierarchyNamedParameters(searchEngine, parameterA);
+      expect(result, unorderedEquals([parameterA, parameterB, parameterC]));
+    }
+
+    {
+      var result = await getHierarchyNamedParameters(searchEngine, parameterB);
+      expect(result, unorderedEquals([parameterA, parameterB, parameterC]));
+    }
+
+    {
+      var result = await getHierarchyNamedParameters(searchEngine, parameterC);
+      expect(result, unorderedEquals([parameterA, parameterB, parameterC]));
+    }
+
+    {
+      var result = await getHierarchyNamedParameters(searchEngine, parameterD);
+      expect(result, unorderedEquals([parameterD, parameterE]));
+    }
+
+    {
+      var result = await getHierarchyNamedParameters(searchEngine, parameterE);
+      expect(result, unorderedEquals([parameterD, parameterE]));
+    }
+  }
+
+  test_getHierarchyNamedParameters_invalid_missing() async {
+    verifyNoTestUnitErrors = false;
+    await _indexTestUnit('''
+class A {
+  foo({p}) {}
+}
+class B extends A {
+  foo() {}
+}
+''');
+    ClassElement classA = findElement('A');
+    ParameterElement parameterA = classA.methods[0].parameters[0];
+
+    var result = await getHierarchyNamedParameters(searchEngine, parameterA);
+    expect(result, unorderedEquals([parameterA]));
+  }
+
+  test_getHierarchyNamedParameters_invalid_notNamed() async {
+    verifyNoTestUnitErrors = false;
+    await _indexTestUnit('''
+class A {
+  foo({p}) {}
+}
+class B extends A {
+  foo(p) {}
+}
+''');
+    ClassElement classA = findElement('A');
+    ParameterElement parameterA = classA.methods[0].parameters[0];
+
+    var result = await getHierarchyNamedParameters(searchEngine, parameterA);
+    expect(result, unorderedEquals([parameterA]));
+  }
+
   test_getMembers() async {
     await _indexTestUnit('''
 class A {