[analysis_server] Support patterns in element references search

Change-Id: I303260c865c70691e0c7723b92c2ee7f09038310
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282664
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/test/search/element_references_test.dart b/pkg/analysis_server/test/search/element_references_test.dart
index 5355f79..fa6d45d 100644
--- a/pkg/analysis_server/test/search/element_references_test.dart
+++ b/pkg/analysis_server/test/search/element_references_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analyzer/src/test_utilities/test_code_format.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -24,9 +25,54 @@
     expect(result.isPotential, isPotential);
   }
 
+  Future<void> assertReferences(
+    String content, {
+    required ElementKind kind,
+    required Map<int, SearchResultKind> resultKinds,
+  }) async {
+    final code = TestCode.parse(content);
+    expect(
+      resultKinds,
+      hasLength(code.ranges.length),
+      reason: "'resultsKinds' should have the same number of items as there "
+          "are ranges in 'content'",
+    );
+
+    addTestFile(code.code);
+    await findElementReferences(offset: code.position.offset, false);
+
+    expect(searchElement!.kind, kind);
+    final expected = resultKinds.entries.map((entry) {
+      final index = entry.key;
+      final kind = entry.value;
+      final range = code.ranges[index].sourceRange;
+      return {
+        'kind': kind,
+        'path': testFile.path,
+        'range': range.offset,
+        'length': range.length,
+      };
+    }).toSet();
+    final actual = results
+        .map((result) => {
+              'kind': result.kind,
+              'path': result.location.file,
+              'range': result.location.offset,
+              'length': result.location.length,
+            })
+        .toSet();
+    expect(actual, equals(expected));
+  }
+
   Future<void> findElementReferences(
-      String search, bool includePotential) async {
-    var offset = findOffset(search);
+    bool includePotential, {
+    // TODO(dantup): Remove 'search' parameter and convert original tests to go
+    //  through 'assertReferences' using parsed code.
+    String? search,
+    int? offset,
+  }) async {
+    assert(search != null || offset != null);
+    offset ??= findOffset(search!);
     await waitForTasksFinished();
     var request = SearchFindElementReferencesParams(
             testFile.path, offset, includePotential)
@@ -58,7 +104,7 @@
   A.named; // 6
 }
 ''');
-    await findElementReferences('named() {}', false);
+    await findElementReferences(search: 'named() {}', false);
     expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
     expect(results, hasLength(6));
     assertHasResult(SearchResultKind.REFERENCE, '.named] 1', 6);
@@ -86,7 +132,7 @@
   x.named(3);
 }
 ''');
-    await findElementReferences('named(p); // A', true);
+    await findElementReferences(search: 'named(p); // A', true);
     expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.INVOCATION, '.named(1)', 6);
@@ -111,7 +157,7 @@
   A.new; // 7
 }
 ''');
-    await findElementReferences('A() {}', false);
+    await findElementReferences(search: 'A() {}', false);
     expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
     expect(results, hasLength(7));
     assertHasResult(SearchResultKind.REFERENCE, '] 1', 0);
@@ -145,7 +191,7 @@
   new B(2);
 }
 ''');
-    await findElementReferences('A(p)', true);
+    await findElementReferences(search: 'A(p)', true);
     expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.INVOCATION, '(1)', 0);
@@ -171,7 +217,7 @@
   a.fff(); // in f()
 }
 ''');
-    await findElementReferences('fff; // declaration', false);
+    await findElementReferences(search: 'fff; // declaration', false);
     expect(searchElement!.kind, ElementKind.FIELD);
     expect(results, hasLength(10));
     assertHasResult(SearchResultKind.WRITE, 'fff); // in constructor');
@@ -204,7 +250,7 @@
 }
 ''');
     {
-      await findElementReferences('fff =>', false);
+      await findElementReferences(search: 'fff =>', false);
       expect(searchElement!.kind, ElementKind.FIELD);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff); // in m()');
@@ -213,7 +259,7 @@
       assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
     }
     {
-      await findElementReferences('fff(x) {}', false);
+      await findElementReferences(search: 'fff(x) {}', false);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff); // in m()');
       assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
@@ -233,7 +279,7 @@
   }
 }
 ''');
-    await findElementReferences('fff); // in constructor', false);
+    await findElementReferences(search: 'fff); // in constructor', false);
     expect(searchElement!.kind, ElementKind.FIELD);
     expect(results, hasLength(3));
     assertHasResult(SearchResultKind.WRITE, 'fff); // in constructor');
@@ -252,7 +298,7 @@
   int get foo => 0;
 }
 ''');
-    await findElementReferences('foo =>', false);
+    await findElementReferences(search: 'foo =>', false);
     expect(searchElement!.kind, ElementKind.FIELD);
     expect(results, hasLength(2));
     assertHasResult(SearchResultKind.READ, 'foo: 0');
@@ -273,7 +319,7 @@
   print(a.mmm); // in f()
 }
 ''');
-    await findElementReferences('mmm(p) {}', false);
+    await findElementReferences(search: 'mmm(p) {}', false);
     expect(searchElement!.kind, ElementKind.METHOD);
     expect(results, hasLength(4));
     assertHasResult(SearchResultKind.INVOCATION, 'mmm(1);');
@@ -293,7 +339,7 @@
   print(a.mmm);
 }
 ''');
-    await findElementReferences('mmm(p) {}', false);
+    await findElementReferences(search: 'mmm(p) {}', false);
     expect(searchElement!.kind, ElementKind.METHOD);
     expect(results, hasLength(2));
     assertHasResult(SearchResultKind.INVOCATION, 'mmm(10);');
@@ -309,7 +355,7 @@
   const E.other() : this.named(); // 4
 }
 ''');
-    await findElementReferences('named(); // 3', false);
+    await findElementReferences(search: 'named(); // 3', false);
     expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
     expect(results, hasLength(3));
     assertHasResult(SearchResultKind.REFERENCE, '.named] 1', 6);
@@ -328,7 +374,7 @@
   const E.other() : this(); // 6
 }
 ''');
-    await findElementReferences('E(); // 5', false);
+    await findElementReferences(search: 'E(); // 5', false);
     expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
     expect(results, hasLength(5));
     assertHasResult(SearchResultKind.REFERENCE, '] 1', 0);
@@ -360,7 +406,7 @@
   e.fff(); // 11
 }
 ''');
-    await findElementReferences('fff; // 01', false);
+    await findElementReferences(search: 'fff; // 01', false);
     expect(searchElement!.kind, ElementKind.FIELD);
     expect(results, hasLength(10));
     assertHasResult(SearchResultKind.WRITE, 'fff); // 02');
@@ -395,7 +441,7 @@
 }
 ''');
     {
-      await findElementReferences('fff =>', false);
+      await findElementReferences(search: 'fff =>', false);
       expect(searchElement!.kind, ElementKind.FIELD);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff; // 1');
@@ -404,7 +450,7 @@
       assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 4');
     }
     {
-      await findElementReferences('fff(_) {}', false);
+      await findElementReferences(search: 'fff(_) {}', false);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff; // 1');
       assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 2');
@@ -429,7 +475,7 @@
   e.foo; // 4
 }
 ''');
-    await findElementReferences('foo() {}', false);
+    await findElementReferences(search: 'foo() {}', false);
     expect(searchElement!.kind, ElementKind.METHOD);
     expect(results, hasLength(4));
     assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 1');
@@ -450,7 +496,7 @@
   E(0).bar();
 }
 ''');
-    await findElementReferences('E on int', false);
+    await findElementReferences(search: 'E on int', false);
     expect(searchElement!.kind, ElementKind.EXTENSION);
     expect(results, hasLength(2));
     assertHasResult(SearchResultKind.REFERENCE, 'E.foo();');
@@ -477,7 +523,7 @@
   E.fff(); // in f()
 }
 ''');
-    await findElementReferences('fff; // declaration', false);
+    await findElementReferences(search: 'fff; // declaration', false);
     expect(searchElement!.kind, ElementKind.FIELD);
     expect(results, hasLength(8));
     // m()
@@ -508,7 +554,7 @@
 }
 ''');
     {
-      await findElementReferences('fff =>', false);
+      await findElementReferences(search: 'fff =>', false);
       expect(searchElement!.kind, ElementKind.FIELD);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff); // in m()');
@@ -517,7 +563,7 @@
       assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
     }
     {
-      await findElementReferences('fff(x) {}', false);
+      await findElementReferences(search: 'fff(x) {}', false);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff); // in m()');
       assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
@@ -542,7 +588,7 @@
 }
 ''');
     {
-      await findElementReferences('fff =>', false);
+      await findElementReferences(search: 'fff =>', false);
       expect(searchElement!.kind, ElementKind.FIELD);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff); // in m()');
@@ -551,7 +597,7 @@
       assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
     }
     {
-      await findElementReferences('fff(x) {}', false);
+      await findElementReferences(search: 'fff(x) {}', false);
       expect(results, hasLength(4));
       assertHasResult(SearchResultKind.READ, 'fff); // in m()');
       assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
@@ -573,7 +619,7 @@
   0.foo; // 4
 }
 ''');
-    await findElementReferences('foo() {}', false);
+    await findElementReferences(search: 'foo() {}', false);
     expect(searchElement!.kind, ElementKind.METHOD);
     expect(results, hasLength(4));
     assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 1');
@@ -590,7 +636,7 @@
   print(fff);
 }
 ''');
-    await findElementReferences('fff(p) {}', false);
+    await findElementReferences(search: 'fff(p) {}', false);
     expect(searchElement!.kind, ElementKind.FUNCTION);
     expect(results, hasLength(2));
     assertHasResult(SearchResultKind.INVOCATION, 'fff(1)');
@@ -614,7 +660,7 @@
     c.fff = 30;
   }
   ''');
-    await findElementReferences('fff; // in B', false);
+    await findElementReferences(search: 'fff; // in B', false);
     expect(searchElement!.kind, ElementKind.FIELD);
     assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
     assertHasResult(SearchResultKind.WRITE, 'fff = 20;');
@@ -638,7 +684,7 @@
   c.mmm(30);
 }
 ''');
-    await findElementReferences('mmm(_) {} // in B', false);
+    await findElementReferences(search: 'mmm(_) {} // in B', false);
     expect(searchElement!.kind, ElementKind.METHOD);
     assertHasResult(SearchResultKind.INVOCATION, 'mmm(10)');
     assertHasResult(SearchResultKind.INVOCATION, 'mmm(20)');
@@ -662,7 +708,7 @@
   C.mmm(30);
 }
 ''');
-    await findElementReferences('mmm(_) {} // in B', false);
+    await findElementReferences(search: 'mmm(_) {} // in B', false);
     expect(searchElement!.kind, ElementKind.METHOD);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.INVOCATION, 'mmm(20)');
@@ -685,7 +731,7 @@
   c.m(p: 3);
 }
 ''');
-    await findElementReferences('p}) {} // in B', false);
+    await findElementReferences(search: 'p}) {} // in B', false);
     expect(searchElement!.kind, ElementKind.PARAMETER);
     assertHasResult(SearchResultKind.REFERENCE, 'p: 1');
     assertHasResult(SearchResultKind.REFERENCE, 'p: 2');
@@ -704,7 +750,7 @@
   }
 }
 ''');
-    await findElementReferences('myLabel; // break', false);
+    await findElementReferences(search: 'myLabel; // break', false);
     expect(searchElement!.kind, ElementKind.LABEL);
     expect(results, hasLength(2));
     assertHasResult(SearchResultKind.REFERENCE, 'myLabel; // continue');
@@ -721,7 +767,7 @@
   vvv();
 }
 ''');
-    await findElementReferences('vvv = 1', false);
+    await findElementReferences(search: 'vvv = 1', false);
     expect(searchElement!.kind, ElementKind.LOCAL_VARIABLE);
     expect(results, hasLength(4));
     assertHasResult(SearchResultKind.READ, 'vvv);');
@@ -735,7 +781,7 @@
 mixin A {}
 class B extends Object with A {} // B
 ''');
-    await findElementReferences('A {}', false);
+    await findElementReferences(search: 'A {}', false);
     expect(searchElement!.kind, ElementKind.MIXIN);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.REFERENCE, 'A {} // B');
@@ -747,7 +793,7 @@
   print(noElement);
 }
 ''');
-    await findElementReferences('noElement', false);
+    await findElementReferences(search: 'noElement', false);
     expect(searchId, isNull);
   }
 
@@ -759,7 +805,7 @@
   fff(10);
 }
 ''');
-    await findElementReferences('fff(p) {}', false);
+    await findElementReferences(search: 'fff(p) {}', false);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.INVOCATION, 'fff(10);');
   }
@@ -773,7 +819,7 @@
   ppp();
 }
 ''');
-    await findElementReferences('ppp) {', false);
+    await findElementReferences(search: 'ppp) {', false);
     expect(searchElement!.kind, ElementKind.PARAMETER);
     expect(results, hasLength(4));
     assertHasResult(SearchResultKind.READ, 'ppp);');
@@ -794,7 +840,7 @@
   }
 }
 ''');
-    await findElementReferences('A {}', false);
+    await findElementReferences(search: 'A {}', false);
     assertHasResult(SearchResultKind.REFERENCE, 'A a = null;');
     expect(getPathString(result.path), '''
 LOCAL_VARIABLE a
@@ -816,7 +862,7 @@
   }
 }
 ''');
-    await findElementReferences('A {}', false);
+    await findElementReferences(search: 'A {}', false);
     assertHasResult(SearchResultKind.REFERENCE, 'A a = null;');
     expect(getPathString(result.path), '''
 LOCAL_VARIABLE a
@@ -838,7 +884,7 @@
   }
 }
 ''');
-    await findElementReferences('foo() {}', false);
+    await findElementReferences(search: 'foo() {}', false);
     assertHasResult(SearchResultKind.INVOCATION, 'foo();');
     expect(getPathString(result.path), '''
 METHOD bar
@@ -859,7 +905,7 @@
   }
 }
 ''');
-    await findElementReferences('foo() {}', false);
+    await findElementReferences(search: 'foo() {}', false);
     assertHasResult(SearchResultKind.INVOCATION, 'foo();');
     expect(getPathString(result.path), '''
 METHOD bar
@@ -878,7 +924,7 @@
   A a = null;
 }
 ''');
-    await findElementReferences('A {}', false);
+    await findElementReferences(search: 'A {}', false);
     assertHasResult(SearchResultKind.REFERENCE, 'A a = null;');
     expect(getPathString(result.path), '''
 LOCAL_VARIABLE a
@@ -887,6 +933,267 @@
 LIBRARY my_lib''');
   }
 
+  Future<void> test_pattern_assignment() async {
+    await assertReferences(
+      kind: ElementKind.PARAMETER,
+      resultKinds: {
+        0: SearchResultKind.WRITE,
+        1: SearchResultKind.READ,
+      },
+      '''
+void f(String ^a, String b) {
+  (b, /*[0*/a/*0]*/) = (/*[1*/a/*1]*/, b);
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_assignment_list() async {
+    await assertReferences(
+      kind: ElementKind.PARAMETER,
+      resultKinds: {
+        0: SearchResultKind.WRITE,
+      },
+      '''
+void f(List<int> x, num ^a) {
+  [/*[0*/a/*0]*/] = x;
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_cast_typeName() async {
+    await assertReferences(
+      kind: ElementKind.CLASS,
+      resultKinds: {
+        0: SearchResultKind.REFERENCE,
+        1: SearchResultKind.REFERENCE,
+      },
+      '''
+String f((num, /*[0*/My^Class/*0]*/) record) {
+  var (i as int, s as /*[1*/MyClass/*1]*/) = record;
+}
+
+class MyClass {}
+''',
+    );
+  }
+
+  Future<void> test_pattern_cast_variable() async {
+    await assertReferences(
+      kind: ElementKind.LOCAL_VARIABLE,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+void f((num, String) record) {
+  var (i as int, s^ as String) = record;
+  print(/*[0*/s/*0]*/);
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_map() async {
+    await assertReferences(
+      kind: ElementKind.LOCAL_VARIABLE,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+void f(x) {
+  switch (x) {
+    case {0: String ^a}:
+      print(/*[0*/a/*0]*/);
+      break;
+  }
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_map_typeArguments() async {
+    await assertReferences(
+      kind: ElementKind.CLASS,
+      resultKinds: {
+        0: SearchResultKind.REFERENCE,
+        1: SearchResultKind.REFERENCE,
+      },
+      '''
+/*[0*/A/*0]*/ f(x) {
+  switch (x) {
+    case <int, /*[1*/A/*1]*/>{0: var a}:
+      return a;
+      break;
+  }
+}
+
+class ^A {}
+''',
+    );
+  }
+
+  Future<void> test_pattern_nullAssert() async {
+    await assertReferences(
+      kind: ElementKind.LOCAL_VARIABLE,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+void f((int?, int?) position) {
+  var (x!, y^!) = position;
+  print(/*[0*/y/*0]*/);
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_nullCheck() async {
+    await assertReferences(
+      kind: ElementKind.LOCAL_VARIABLE,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+void f(String? maybeString) {
+  switch (maybeString) {
+    case var ^s?:
+      print(/*[0*/s/*0]*/);
+  }
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_object_field() async {
+    await assertReferences(
+      kind: ElementKind.FIELD,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+double calculateArea(Shape shape) =>
+  switch (shape) {
+    Square(/*[0*/length/*0]*/: var l) => l * l,
+  };
+
+class Shape { }
+class Square extends Shape {
+  double get len^gth => 0;
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_object_fieldName_explicit() async {
+    await assertReferences(
+      kind: ElementKind.FIELD,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+double calculateArea(Object a) =>
+  switch (a) {
+    Square(/*[0*/length/*0]*/: var l) => l * l,
+  };
+
+class Square {
+  double len^gth = 0;
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_object_fieldName_implicit() async {
+    await assertReferences(
+      kind: ElementKind.FIELD,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+double calculateArea(Object a) =>
+  switch (a) {
+    Square(/*[0*//*0]*/:var length) => length * length,
+  };
+
+class Square {
+  double len^gth = 0;
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_object_typeName() async {
+    await assertReferences(
+      kind: ElementKind.CLASS,
+      resultKinds: {
+        0: SearchResultKind.REFERENCE,
+      },
+      '''
+double calculateArea(Object a) =>
+  switch (a) {
+    /*[0*/Square/*0]*/(length: var l) => l * l,
+  };
+
+class Sq^uare {
+  double get length => 0;
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_object_variable() async {
+    await assertReferences(
+      kind: ElementKind.LOCAL_VARIABLE,
+      resultKinds: {
+        0: SearchResultKind.READ,
+        1: SearchResultKind.READ,
+      },
+      '''
+double calculateArea(Shape shape) =>
+  switch (shape) {
+    Square(length: var ^l) => /*[0*/l/*0]*/ * /*[1*/l/*1]*/,
+  };
+
+class Shape { }
+class Square extends Shape {
+  double get length => 0;
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_record_fieldAssignment() async {
+    await assertReferences(
+      kind: ElementKind.PARAMETER,
+      resultKinds: {
+        0: SearchResultKind.WRITE,
+      },
+      '''
+void f(({int foo}) x, num ^a) {
+  (foo: /*[0*/a/*0]*/,) = x;
+}
+''',
+    );
+  }
+
+  Future<void> test_pattern_relational_variable() async {
+    await assertReferences(
+      kind: ElementKind.LOCAL_VARIABLE,
+      resultKinds: {
+        0: SearchResultKind.READ,
+      },
+      '''
+String f(int char) {
+  const ze^ro = 0;
+  return switch (char) {
+    == /*[0*/zero/*0]*/ => 'zero'
+  };
+}
+''',
+    );
+  }
+
   Future<void> test_potential_disabled() async {
     addTestFile('''
 class A {
@@ -897,7 +1204,7 @@
   p.test(2);
 }
 ''');
-    await findElementReferences('test(p) {}', false);
+    await findElementReferences(search: 'test(p) {}', false);
     assertHasResult(SearchResultKind.INVOCATION, 'test(1);');
     assertNoResult(SearchResultKind.INVOCATION, 'test(2);');
   }
@@ -913,7 +1220,7 @@
   print(p.test); // p
 }
 ''');
-    await findElementReferences('test; // declaration', true);
+    await findElementReferences(search: 'test; // declaration', true);
     {
       assertHasResult(SearchResultKind.WRITE, 'test = 1;');
       expect(result.isPotential, isFalse);
@@ -938,7 +1245,7 @@
   p.test(2);
 }
 ''');
-    await findElementReferences('test(p) {}', true);
+    await findElementReferences(search: 'test(p) {}', true);
     {
       assertHasResult(SearchResultKind.INVOCATION, 'test(1);');
       expect(result.isPotential, isFalse);
@@ -966,7 +1273,7 @@
   b.test(3);
 }
 ''');
-    await findElementReferences('test(_) {} // of Derived', true);
+    await findElementReferences(search: 'test(_) {} // of Derived', true);
     assertHasRef(SearchResultKind.INVOCATION, 'test(1);', true);
     assertHasRef(SearchResultKind.INVOCATION, 'test(2);', false);
     assertHasRef(SearchResultKind.INVOCATION, 'test(3);', true);
@@ -980,7 +1287,7 @@
   ppp.Stream b;
 }
 ''');
-    await findElementReferences('ppp;', false);
+    await findElementReferences(search: 'ppp;', false);
     final searchElement = this.searchElement!;
     expect(searchElement.kind, ElementKind.PREFIX);
     expect(searchElement.name, 'ppp');
@@ -1000,7 +1307,7 @@
   foo(0, test: 2, 1);
 }
 ''');
-    await findElementReferences('test})', false);
+    await findElementReferences(search: 'test})', false);
     expect(searchElement!.kind, ElementKind.PARAMETER);
     expect(results, hasLength(2));
     assertHasResult(SearchResultKind.READ, 'test;');
@@ -1017,7 +1324,7 @@
   vvv();
 }
 ''');
-    await findElementReferences('vvv = 1', false);
+    await findElementReferences(search: 'vvv = 1', false);
     expect(searchElement!.kind, ElementKind.TOP_LEVEL_VARIABLE);
     expect(results, hasLength(4));
     assertHasResult(SearchResultKind.READ, 'vvv);');
@@ -1036,14 +1343,14 @@
 }
 ''');
     {
-      await findElementReferences('vvv =>', false);
+      await findElementReferences(search: 'vvv =>', false);
       expect(searchElement!.kind, ElementKind.TOP_LEVEL_VARIABLE);
       expect(results, hasLength(2));
       assertHasResult(SearchResultKind.READ, 'vvv);');
       assertHasResult(SearchResultKind.WRITE, 'vvv = 1;');
     }
     {
-      await findElementReferences('vvv(x) {}', false);
+      await findElementReferences(search: 'vvv(x) {}', false);
       expect(results, hasLength(2));
       assertHasResult(SearchResultKind.READ, 'vvv);');
       assertHasResult(SearchResultKind.WRITE, 'vvv = 1;');
@@ -1057,7 +1364,7 @@
   int b = 2;
 }
 ''');
-    await findElementReferences('int a', false);
+    await findElementReferences(search: 'int a', false);
     expect(searchElement!.kind, ElementKind.CLASS);
     assertHasResult(SearchResultKind.REFERENCE, 'int a');
     assertHasResult(SearchResultKind.REFERENCE, 'int b');
@@ -1069,7 +1376,7 @@
 void f(F f) {
 }
 ''');
-    await findElementReferences('F =', false);
+    await findElementReferences(search: 'F =', false);
     expect(searchElement!.kind, ElementKind.TYPE_ALIAS);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.REFERENCE, 'F f');
@@ -1082,13 +1389,13 @@
 void(A<String> a) {}
 ''');
     // Can find `A`.
-    await findElementReferences('A<T> =', false);
+    await findElementReferences(search: 'A<T> =', false);
     expect(searchElement!.kind, ElementKind.TYPE_ALIAS);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.REFERENCE, 'A<String>');
 
     // Can find in `A`.
-    await findElementReferences('int,', false);
+    await findElementReferences(search: 'int,', false);
     expect(searchElement!.kind, ElementKind.CLASS);
     assertHasResult(SearchResultKind.REFERENCE, 'int,');
   }
@@ -1099,7 +1406,7 @@
 void f(F f) {
 }
 ''');
-    await findElementReferences('F()', false);
+    await findElementReferences(search: 'F()', false);
     expect(searchElement!.kind, ElementKind.TYPE_ALIAS);
     expect(results, hasLength(1));
     assertHasResult(SearchResultKind.REFERENCE, 'F f');
@@ -1112,7 +1419,7 @@
   T m() => null;
 }
 ''');
-    await findElementReferences('T> {', false);
+    await findElementReferences(search: 'T> {', false);
     expect(searchElement!.kind, ElementKind.TYPE_PARAMETER);
     expect(results, hasLength(2));
     assertHasResult(SearchResultKind.REFERENCE, 'T f;');
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 690064c..6b580d4 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -87,7 +87,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 259;
+  static const int DATA_VERSION = 260;
 
   /// The number of exception contexts allowed to write. Once this field is
   /// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
index 80f9029..1365e1b 100644
--- a/pkg/analyzer/lib/src/dart/analysis/search.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
@@ -738,6 +738,11 @@
     PatternVariableElementImpl element,
     SearchedFiles searchedFiles,
   ) async {
+    String path = element.source.fullName;
+    if (!searchedFiles.add(path, this)) {
+      return const <SearchResult>[];
+    }
+
     var rootVariable = element.rootVariable;
     var transitiveVariables = rootVariable is JoinPatternVariableElementImpl
         ? rootVariable.transitiveVariables