// 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: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';

import 'abstract_search_domain.dart';

void main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(ElementReferencesTest);
  });
}

@reflectiveTest
class ElementReferencesTest extends AbstractSearchDomainTest {
  Element? searchElement;

  void assertHasRef(SearchResultKind kind, String search, bool isPotential) {
    assertHasResult(kind, search);
    expect(result.isPotential, isPotential);
  }

  Future<void> assertReferences(
    String content, {
    required ElementKind kind,
    required Map<int, SearchResultKind> resultKinds,
  }) async {
    var code = TestCode.parseNormalized(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);
    var expected = resultKinds.entries.map((entry) {
      var index = entry.key;
      var kind = entry.value;
      var range = code.ranges[index].sourceRange;
      return {
        'kind': kind,
        'path': testFile.path,
        'range': range.offset,
        'length': range.length,
      };
    }).toSet();
    var 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(
    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,
    ).toRequest('0', clientUriConverter: server.uriConverter);
    var response = await handleSuccessfulRequest(request);
    var result = SearchFindElementReferencesResult.fromResponse(
      response,
      clientUriConverter: server.uriConverter,
    );
    searchId = result.id;
    searchElement = result.element;
    if (searchId != null) {
      await waitForSearchResults();
    }
  }

  Future<void> test_class_constructor_named() async {
    addTestFile('''
/// [new A.named] 1
class A {
  A.named() {}
  A.other() : this.named(); // 2
}

class B extends A {
  B() : super.named(); // 3
  factory B.other() = A.named; // 4
}

void f() {
  A.named(); // 5
  A.named; // 6
}
''');
    await findElementReferences(search: 'named() {}', false);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(6));
    assertHasResult(SearchResultKind.REFERENCE, '.named] 1', 6);
    assertHasResult(SearchResultKind.INVOCATION, '.named(); // 2', 6);
    assertHasResult(SearchResultKind.INVOCATION, '.named(); // 3', 6);
    assertHasResult(SearchResultKind.REFERENCE, '.named; // 4', 6);
    assertHasResult(SearchResultKind.INVOCATION, '.named(); // 5', 6);
    assertHasResult(SearchResultKind.REFERENCE, '.named; // 6', 6);
  }

  Future<void> test_class_constructor_named_potential() async {
    // Constructors in other classes shouldn't be considered potential matches.
    // Unresolved method calls should also not be considered potential matches,
    // because constructor call sites are statically bound to their targets.
    addTestFile('''
class A {
  A.named(p); // A
}
class B {
  B.named(p);
}
f(x) {
  new A.named(1);
  new B.named(2);
  x.named(3);
}
''');
    await findElementReferences(search: 'named(p); // A', true);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(1));
    assertHasResult(SearchResultKind.INVOCATION, '.named(1)', 6);
  }

  Future<void> test_class_constructor_unnamed() async {
    addTestFile('''
/// [new A] 1
/// [A.new] 2
class A {
  A() {}
  A.other() : this(); // 3
}

class B extends A {
  B() : super(); // 4
  factory B.other() = A; // 5
}

void f() {
  A(); // 6
  A.new; // 7
}
''');
    await findElementReferences(search: 'A() {}', false);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(7));
    assertHasResult(SearchResultKind.REFERENCE, '] 1', 0);
    assertHasResult(SearchResultKind.REFERENCE, '.new] 2', 4);
    assertHasResult(SearchResultKind.INVOCATION, '(); // 3', 0);
    assertHasResult(SearchResultKind.INVOCATION, '(); // 4', 0);
    assertHasResult(SearchResultKind.REFERENCE, '; // 5', 0);
    assertHasResult(SearchResultKind.INVOCATION, '(); // 6', 0);
    assertHasResult(SearchResultKind.REFERENCE, '.new; // 7', 4);
  }

  Future<void> test_class_constructor_unnamed_potential() async {
    // Constructors in other classes shouldn't be considered potential matches,
    // even if they are also unnamed (since constructor call sites are
    // statically bound to their targets).
    // Also, assignments to local variables shouldn't be considered potential
    // matches.
    addTestFile('''
class A {
  A(p); // A
}
class B {
  B(p);
  foo() {
    int k;
    k = 3;
  }
}
void f() {
  new A(1);
  new B(2);
}
''');
    await findElementReferences(search: 'A(p)', true);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(1));
    assertHasResult(SearchResultKind.INVOCATION, '(1)', 0);
  }

  Future<void> test_class_field_explicit() async {
    addTestFile('''
class A {
  var fff; // declaration
  A(this.fff); // in constructor
  A.named() : fff = 1;
  m() {
    fff = 2;
    fff += 3;
    print(fff); // in m()
    fff(); // in m()
  }
}
void f(A a) {
  a.fff = 20;
  a.fff += 30;
  print(a.fff); // in f()
  a.fff(); // in f()
}
''');
    await findElementReferences(search: 'fff; // declaration', false);
    expect(searchElement!.kind, ElementKind.FIELD);
    expect(results, hasLength(10));
    assertHasResult(SearchResultKind.WRITE, 'fff); // in constructor');
    assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
    // m()
    assertHasResult(SearchResultKind.WRITE, 'fff = 2;');
    assertHasResult(SearchResultKind.WRITE, 'fff += 3;');
    assertHasResult(SearchResultKind.READ, 'fff); // in m()');
    assertHasResult(SearchResultKind.READ, 'fff(); // in m()');
    // f()
    assertHasResult(SearchResultKind.WRITE, 'fff = 20;');
    assertHasResult(SearchResultKind.WRITE, 'fff += 30;');
    assertHasResult(SearchResultKind.READ, 'fff); // in f()');
    assertHasResult(SearchResultKind.READ, 'fff(); // in f()');
  }

  Future<void> test_class_field_implicit() async {
    addTestFile('''
class A {
  var  get fff => null;
  void set fff(x) {}
  m() {
    print(fff); // in m()
    fff = 1;
  }
}
void f(A a) {
  print(a.fff); // in f()
  a.fff = 10;
}
''');
    {
      await findElementReferences(search: 'fff =>', false);
      expect(searchElement!.kind, ElementKind.FIELD);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff); // in m()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
      assertHasResult(SearchResultKind.READ, 'fff); // in f()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
    }
    {
      await findElementReferences(search: 'fff(x) {}', false);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff); // in m()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
      assertHasResult(SearchResultKind.READ, 'fff); // in f()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
    }
  }

  Future<void> test_class_field_inFormalParameter() async {
    addTestFile('''
class A {
  var fff; // declaration
  A(this.fff); // in constructor
  m() {
    fff = 2;
    print(fff); // in m()
  }
}
''');
    await findElementReferences(search: 'fff); // in constructor', false);
    expect(searchElement!.kind, ElementKind.FIELD);
    expect(results, hasLength(3));
    assertHasResult(SearchResultKind.WRITE, 'fff); // in constructor');
    assertHasResult(SearchResultKind.WRITE, 'fff = 2;');
    assertHasResult(SearchResultKind.READ, 'fff); // in m()');
  }

  Future<void> test_class_getter_in_objectPattern() async {
    addTestFile('''
void f(Object? x) {
  if (x case A(foo: 0)) {}
  if (x case A(: var foo)) {}
}

class A {
  int get foo => 0;
}
''');
    await findElementReferences(search: 'foo =>', false);
    expect(searchElement!.kind, ElementKind.FIELD);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'foo: 0');
    assertHasResult(SearchResultKind.REFERENCE, ': var foo');
  }

  Future<void> test_class_method() async {
    addTestFile('''
class A {
  mmm(p) {}
  m() {
    mmm(1);
    print(mmm); // in m()
  }
}
void f(A a) {
  a.mmm(10);
  print(a.mmm); // in f()
}
''');
    await findElementReferences(search: 'mmm(p) {}', false);
    expect(searchElement!.kind, ElementKind.METHOD);
    expect(results, hasLength(4));
    assertHasResult(SearchResultKind.INVOCATION, 'mmm(1);');
    assertHasResult(SearchResultKind.REFERENCE, 'mmm); // in m()');
    assertHasResult(SearchResultKind.INVOCATION, 'mmm(10);');
    assertHasResult(SearchResultKind.REFERENCE, 'mmm); // in f()');
  }

  Future<void> test_class_method_propagatedType() async {
    addTestFile('''
class A {
  mmm(p) {}
}
void f() {
  var a = new A();
  a.mmm(10);
  print(a.mmm);
}
''');
    await findElementReferences(search: 'mmm(p) {}', false);
    expect(searchElement!.kind, ElementKind.METHOD);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.INVOCATION, 'mmm(10);');
    assertHasResult(SearchResultKind.REFERENCE, 'mmm);');
  }

  Future<void> test_enum_constructor_named() async {
    addTestFile('''
/// [new E.named] 1
enum E {
  v.named(); // 2
  const E.named(); // 3
  const E.other() : this.named(); // 4
}
''');
    await findElementReferences(search: 'named(); // 3', false);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(3));
    assertHasResult(SearchResultKind.REFERENCE, '.named] 1', 6);
    assertHasResult(SearchResultKind.INVOCATION, '.named(); // 2', 6);
    assertHasResult(SearchResultKind.INVOCATION, '.named(); // 4', 6);
  }

  Future<void> test_enum_constructor_unnamed() async {
    addTestFile('''
/// [new E] 1
enum E {
  v1, // 2
  v2(), // 3
  v3.new(); // 4
  const E(); // 5
  const E.other() : this(); // 6
}
''');
    await findElementReferences(search: 'E(); // 5', false);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(5));
    assertHasResult(SearchResultKind.REFERENCE, '] 1', 0);
    assertHasResult(SearchResultKind.INVOCATION, ', // 2', 0);
    assertHasResult(SearchResultKind.INVOCATION, '(), // 3', 0);
    assertHasResult(SearchResultKind.INVOCATION, '.new(); // 4', 4);
    assertHasResult(SearchResultKind.INVOCATION, '(); // 6', 0);
  }

  Future<void> test_enum_field_explicit() async {
    addTestFile('''
enum E {
  v;
  var fff; // 01
  E(this.fff); // 02
  E.named() : fff = 0; // 03
  void foo() {
    fff = 0; // 04
    fff += 0; // 05
    fff; // 06
    fff(); // 07
  }
}

void f(E e) {
  e.fff = 0; // 08
  e.fff += 0; // 09
  e.fff; // 10
  e.fff(); // 11
}
''');
    await findElementReferences(search: 'fff; // 01', false);
    expect(searchElement!.kind, ElementKind.FIELD);
    expect(results, hasLength(10));
    assertHasResult(SearchResultKind.WRITE, 'fff); // 02');
    assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 03');
    // foo()
    assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 04');
    assertHasResult(SearchResultKind.WRITE, 'fff += 0; // 05');
    assertHasResult(SearchResultKind.READ, 'fff; // 06');
    assertHasResult(SearchResultKind.READ, 'fff(); // 07');
    // f()
    assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 08');
    assertHasResult(SearchResultKind.WRITE, 'fff += 0; // 09');
    assertHasResult(SearchResultKind.READ, 'fff; // 10');
    assertHasResult(SearchResultKind.READ, 'fff(); // 11');
  }

  Future<void> test_enum_field_implicit() async {
    addTestFile('''
enum E {
  v;
  int get fff => 0;
  void set fff(_) {}
  void foo() {
    fff; // 1
    fff = 0; // 2
  }
}

void f(E e) {
  e.fff; // 3
  e.fff = 0; // 4
}
''');
    {
      await findElementReferences(search: 'fff =>', false);
      expect(searchElement!.kind, ElementKind.FIELD);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff; // 1');
      assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 2');
      assertHasResult(SearchResultKind.READ, 'fff; // 3');
      assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 4');
    }
    {
      await findElementReferences(search: 'fff(_) {}', false);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff; // 1');
      assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 2');
      assertHasResult(SearchResultKind.READ, 'fff; // 3');
      assertHasResult(SearchResultKind.WRITE, 'fff = 0; // 4');
    }
  }

  Future<void> test_enum_method() async {
    addTestFile('''
enum E {
  v;
  void foo() {}
  void bar() {
    foo(); // 1
    this.foo(); // 2
  }
}

void f(E e) {
  e.foo(); // 3
  e.foo; // 4
}
''');
    await findElementReferences(search: 'foo() {}', false);
    expect(searchElement!.kind, ElementKind.METHOD);
    expect(results, hasLength(4));
    assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 1');
    assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 2');
    assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 3');
    assertHasResult(SearchResultKind.REFERENCE, 'foo; // 4');
  }

  Future<void> test_extension() async {
    addTestFile('''
extension E on int {
  static void foo() {}
  void bar() {}
}

void f() {
  E.foo();
  E(0).bar();
}
''');
    await findElementReferences(search: 'E on int', false);
    expect(searchElement!.kind, ElementKind.EXTENSION);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'E.foo();');
    assertHasResult(SearchResultKind.REFERENCE, 'E(0)');
  }

  Future<void> test_extension_field_explicit_static() async {
    addTestFile('''
extension E on int {
  static var fff; // declaration

  void m() {
    fff = 2;
    fff += 3;
    print(fff); // in m()
    fff(); // in m()
  }
}

void f() {
  E.fff = 20;
  E.fff += 30;
  print(E.fff); // in f()
  E.fff(); // in f()
}
''');
    await findElementReferences(search: 'fff; // declaration', false);
    expect(searchElement!.kind, ElementKind.FIELD);
    expect(results, hasLength(8));
    // m()
    assertHasResult(SearchResultKind.WRITE, 'fff = 2;');
    assertHasResult(SearchResultKind.WRITE, 'fff += 3;');
    assertHasResult(SearchResultKind.READ, 'fff); // in m()');
    assertHasResult(SearchResultKind.READ, 'fff(); // in m()');
    // f()
    assertHasResult(SearchResultKind.WRITE, 'fff = 20;');
    assertHasResult(SearchResultKind.WRITE, 'fff += 30;');
    assertHasResult(SearchResultKind.READ, 'fff); // in f()');
    assertHasResult(SearchResultKind.READ, 'fff(); // in f()');
  }

  Future<void> test_extension_field_implicit_instance() async {
    addTestFile('''
extension E on int {
  var get fff => null;
  set fff(x) {}
  m() {
    print(fff); // in m()
    fff = 1;
  }
}
void f() {
  print(0.fff); // in f()
  0.fff = 10;
}
''');
    {
      await findElementReferences(search: 'fff =>', false);
      expect(searchElement!.kind, ElementKind.FIELD);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff); // in m()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
      assertHasResult(SearchResultKind.READ, 'fff); // in f()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
    }
    {
      await findElementReferences(search: 'fff(x) {}', false);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff); // in m()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
      assertHasResult(SearchResultKind.READ, 'fff); // in f()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
    }
  }

  Future<void> test_extension_field_implicit_static() async {
    addTestFile('''
extension E on int {
  static var get fff => null;
  static set fff(x) {}
  m() {
    print(fff); // in m()
    fff = 1;
  }
}
void f() {
  print(E.fff); // in f()
  E.fff = 10;
}
''');
    {
      await findElementReferences(search: 'fff =>', false);
      expect(searchElement!.kind, ElementKind.FIELD);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff); // in m()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
      assertHasResult(SearchResultKind.READ, 'fff); // in f()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
    }
    {
      await findElementReferences(search: 'fff(x) {}', false);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'fff); // in m()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 1;');
      assertHasResult(SearchResultKind.READ, 'fff); // in f()');
      assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
    }
  }

  Future<void> test_extension_method() async {
    addTestFile('''
extension E on int {
  void foo() {}
}

void f() {
  E(0).foo(); // 1
  E(0).foo; // 2
  0.foo(); // 3
  0.foo; // 4
}
''');
    await findElementReferences(search: 'foo() {}', false);
    expect(searchElement!.kind, ElementKind.METHOD);
    expect(results, hasLength(4));
    assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 1');
    assertHasResult(SearchResultKind.REFERENCE, 'foo; // 2');
    assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 3');
    assertHasResult(SearchResultKind.REFERENCE, 'foo; // 4');
  }

  Future<void> test_extensionType_constructor_named() async {
    addTestFile('''
/// [new A.named] 1
extension type A(int it) {
  A.named() : this(0);
  A.other() : this.named(); // 2
}

void f() {
  A.named(); // 3
  A.named; // 4
}
''');
    await findElementReferences(search: 'named() :', false);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(4));
    assertHasResult(SearchResultKind.REFERENCE, '.named] 1', 6);
    assertHasResult(SearchResultKind.INVOCATION, '.named(); // 2', 6);
    assertHasResult(SearchResultKind.INVOCATION, '.named(); // 3', 6);
    assertHasResult(SearchResultKind.REFERENCE, '.named; // 4', 6);
  }

  Future<void> test_extensionType_constructor_unnamed() async {
    addTestFile('''
/// [new A] 1
/// [A.new] 2
extension type A.named(int it) {
  A() : named(0);
  A.other() : this(); // 3
}

void f() {
  A(); // 4
  A.new; // 5
}
''');
    await findElementReferences(search: 'A() :', false);
    expect(searchElement!.kind, ElementKind.CONSTRUCTOR);
    expect(results, hasLength(5));
    assertHasResult(SearchResultKind.REFERENCE, '] 1', 0);
    assertHasResult(SearchResultKind.REFERENCE, '.new] 2', 4);
    assertHasResult(SearchResultKind.INVOCATION, '(); // 3', 0);
    assertHasResult(SearchResultKind.INVOCATION, '(); // 4', 0);
    assertHasResult(SearchResultKind.REFERENCE, '.new; // 5', 4);
  }

  Future<void> test_extensionType_field_explicit_static() async {
    addTestFile('''
extension E(int it) {
  static dynamic foo; // declaration

  void m() {
    foo; // in m()
    foo(); // in m()
    foo = 1;
    foo += 2;
  }
}

void f() {
  E.foo; // in f()
  E.foo(); // in f()
  E.foo = 10;
  E.foo += 20;
}
''');
    await findElementReferences(search: 'foo; // declaration', false);
    expect(searchElement!.kind, ElementKind.FIELD);
    expect(results, hasLength(8));
    // m()
    assertHasResult(SearchResultKind.READ, 'foo; // in m()');
    assertHasResult(SearchResultKind.READ, 'foo(); // in m()');
    assertHasResult(SearchResultKind.WRITE, 'foo = 1;');
    assertHasResult(SearchResultKind.WRITE, 'foo += 2;');
    // f()
    assertHasResult(SearchResultKind.READ, 'foo; // in f()');
    assertHasResult(SearchResultKind.READ, 'foo(); // in f()');
    assertHasResult(SearchResultKind.WRITE, 'foo = 10;');
    assertHasResult(SearchResultKind.WRITE, 'foo += 20;');
  }

  Future<void> test_extensionType_field_implicit() async {
    addTestFile('''
extension type A(int it) {
  int get foo => 0;
  set foo(int x) {}
  void m() {
    foo; // in m()
    foo = 1;
  }
}
void f(A a) {
  a.foo; // in f()
  a.foo = 10;
}
''');
    {
      await findElementReferences(search: 'foo =>', false);
      expect(searchElement!.kind, ElementKind.FIELD);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'foo; // in m()');
      assertHasResult(SearchResultKind.WRITE, 'foo = 1;');
      assertHasResult(SearchResultKind.READ, 'foo; // in f()');
      assertHasResult(SearchResultKind.WRITE, 'foo = 10;');
    }
    {
      await findElementReferences(search: 'foo(int x) {}', false);
      expect(results, hasLength(4));
      assertHasResult(SearchResultKind.READ, 'foo; // in m()');
      assertHasResult(SearchResultKind.WRITE, 'foo = 1;');
      assertHasResult(SearchResultKind.READ, 'foo; // in f()');
      assertHasResult(SearchResultKind.WRITE, 'foo = 10;');
    }
  }

  Future<void> test_extensionType_method() async {
    addTestFile('''
extension type E(int it) {
  void foo() {}
}

void f(E e) {
  e.foo(); // 1
  e.foo; // 2
}
''');
    await findElementReferences(search: 'foo() {}', false);
    expect(searchElement!.kind, ElementKind.METHOD);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.INVOCATION, 'foo(); // 1');
    assertHasResult(SearchResultKind.REFERENCE, 'foo; // 2');
  }

  Future<void> test_function() async {
    addTestFile('''
fff(p) {}
void f() {
  fff(1);
  print(fff);
}
''');
    await findElementReferences(search: 'fff(p) {}', false);
    expect(searchElement!.kind, ElementKind.FUNCTION);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.INVOCATION, 'fff(1)');
    assertHasResult(SearchResultKind.REFERENCE, 'fff);');
  }

  Future<void> test_hierarchy_field_explicit() async {
    addTestFile('''
  class A {
    int fff; // in A
  }
  class B extends A {
    int fff; // in B
  }
  class C extends B {
    int fff; // in C
  }
  void f(A a, B b, C c) {
    a.fff = 10;
    b.fff = 20;
    c.fff = 30;
  }
  ''');
    await findElementReferences(search: 'fff; // in B', false);
    expect(searchElement!.kind, ElementKind.FIELD);
    assertHasResult(SearchResultKind.WRITE, 'fff = 10;');
    assertHasResult(SearchResultKind.WRITE, 'fff = 20;');
    assertHasResult(SearchResultKind.WRITE, 'fff = 30;');
  }

  Future<void> test_hierarchy_method() async {
    addTestFile('''
class A {
  mmm(_) {} // in A
}
class B extends A {
  mmm(_) {} // in B
}
class C extends B {
  mmm(_) {} // in C
}
void f(A a, B b, C c) {
  a.mmm(10);
  b.mmm(20);
  c.mmm(30);
}
''');
    await findElementReferences(search: 'mmm(_) {} // in B', false);
    expect(searchElement!.kind, ElementKind.METHOD);
    assertHasResult(SearchResultKind.INVOCATION, 'mmm(10)');
    assertHasResult(SearchResultKind.INVOCATION, 'mmm(20)');
    assertHasResult(SearchResultKind.INVOCATION, 'mmm(30)');
  }

  Future<void> test_hierarchy_method_static() async {
    addTestFile('''
class A {
  static void mmm(_) {} // in A
}
class B extends A {
  static void mmm(_) {} // in B
}
class C extends B {
  static void mmm(_) {} // in C
}
void f() {
  A.mmm(10);
  B.mmm(20);
  C.mmm(30);
}
''');
    await findElementReferences(search: 'mmm(_) {} // in B', false);
    expect(searchElement!.kind, ElementKind.METHOD);
    expect(results, hasLength(1));
    assertHasResult(SearchResultKind.INVOCATION, 'mmm(20)');
  }

  Future<void> 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
}
void f(A a, B b, C c) {
  a.m(p: 1);
  b.m(p: 2);
  c.m(p: 3);
}
''');
    await findElementReferences(search: 'p}) {} // in B', false);
    expect(searchElement!.kind, ElementKind.PARAMETER);
    assertHasResult(SearchResultKind.REFERENCE, 'p: 1');
    assertHasResult(SearchResultKind.REFERENCE, 'p: 2');
    assertHasResult(SearchResultKind.REFERENCE, 'p: 3');
  }

  Future<void> test_label() async {
    await setPriorityFiles2([testFile]);
    addTestFile('''
void f() {
myLabel:
  for (int i = 0; i < 10; i++) {
    if (i == 2) {
      continue myLabel; // continue
    }
    break myLabel; // break
  }
}
''');
    await findElementReferences(search: 'myLabel; // break', false);
    expect(searchElement!.kind, ElementKind.LABEL);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'myLabel; // continue');
    assertHasResult(SearchResultKind.REFERENCE, 'myLabel; // break');
  }

  Future<void> test_localVariable() async {
    await setPriorityFiles2([testFile]);
    addTestFile('''
void f() {
  var vvv = 1;
  print(vvv);
  vvv += 3;
  vvv = 2;
  vvv();
}
''');
    await findElementReferences(search: 'vvv = 1', false);
    expect(searchElement!.kind, ElementKind.LOCAL_VARIABLE);
    expect(results, hasLength(4));
    assertHasResult(SearchResultKind.READ, 'vvv);');
    assertHasResult(SearchResultKind.READ_WRITE, 'vvv += 3');
    assertHasResult(SearchResultKind.WRITE, 'vvv = 2');
    assertHasResult(SearchResultKind.READ, 'vvv();');
  }

  Future<void> test_localVariable_inNullAwareElements() async {
    await setPriorityFiles2([testFile]);
    addTestFile('''
void f(bool t) {
  int? vvv = t ? 0 : null;
  List<int> list = [?vvv];
  Set<int> set = {?vvv};
  Map<int, String> mapKey = {?vvv: "value"};
  Map<String, int> mapValue = {"key": ?vvv, /* mapValue */};
  Map<int, int> mapKeyValue1 = {?vvv: vvv, /* mapKeyValue1 */}; // Using promotion.
  Map<int, int> mapKeyValue2 = {?vvv: ?vvv, /* mapKeyValue2 */}; // Ignoring promotion.
}
''');
    await findElementReferences(search: 'vvv = t ? 0 : null', false);
    expect(searchElement!.kind, ElementKind.LOCAL_VARIABLE);
    expect(results, hasLength(8));
    assertHasResult(SearchResultKind.READ, 'vvv];');
    assertHasResult(SearchResultKind.READ, 'vvv};');
    assertHasResult(SearchResultKind.READ, 'vvv: "value"');
    assertHasResult(SearchResultKind.READ, 'vvv, /* mapValue */};');
    assertHasResult(SearchResultKind.READ, 'vvv: vvv');
    assertHasResult(SearchResultKind.READ, 'vvv, /* mapKeyValue1 */};');
    assertHasResult(SearchResultKind.READ, 'vvv: ?vvv');
    assertHasResult(SearchResultKind.READ, 'vvv, /* mapKeyValue2 */};');
  }

  Future<void> test_mixin() async {
    addTestFile('''
mixin A {}
class B extends Object with A {} // B
''');
    await findElementReferences(search: 'A {}', false);
    expect(searchElement!.kind, ElementKind.MIXIN);
    expect(results, hasLength(1));
    assertHasResult(SearchResultKind.REFERENCE, 'A {} // B');
  }

  Future<void> test_noElement() async {
    addTestFile('''
void f() {
  print(noElement);
}
''');
    await findElementReferences(search: 'noElement', false);
    expect(searchId, isNull);
  }

  Future<void> test_oneUnit_zeroLibraries() async {
    addTestFile('''
part of lib;
fff(p) {}
void f() {
  fff(10);
}
''');
    await findElementReferences(search: 'fff(p) {}', false);
    expect(results, isEmpty);
  }

  Future<void> test_parameter() async {
    addTestFile('''
void f(ppp) {
  print(ppp);
  ppp += 3;
  ppp = 2;
  ppp();
}
''');
    await findElementReferences(search: 'ppp) {', false);
    expect(searchElement!.kind, ElementKind.PARAMETER);
    expect(results, hasLength(4));
    assertHasResult(SearchResultKind.READ, 'ppp);');
    assertHasResult(SearchResultKind.READ_WRITE, 'ppp += 3');
    assertHasResult(SearchResultKind.WRITE, 'ppp = 2');
    assertHasResult(SearchResultKind.READ, 'ppp();');
  }

  Future<void> test_parameter_generic_declaration() async {
    addTestFile('''
void f() {
  B().m(p: null); // 1
  B().m(p: null); // 2
}

class A<T> {
  void m({T? p}) {} // 3
}

class B extends A<String> {}
''');
    await findElementReferences(search: 'p}) {} // 3', false);
    expect(searchElement!.kind, ElementKind.PARAMETER);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'p: null); // 1');
    assertHasResult(SearchResultKind.REFERENCE, 'p: null); // 2');
  }

  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/60200')
  Future<void> test_parameter_generic_invocation() async {
    addTestFile('''
void f() {
  B().m(p: null); // 1
  B().m(p: null); // 2
}

class A<T> {
  void m({T? p}) {} // 3
}

class B extends A<String> {}
''');
    await findElementReferences(search: 'p: null); // 1', false);
    expect(searchElement!.kind, ElementKind.PARAMETER);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'p: null); // 1');
    assertHasResult(SearchResultKind.REFERENCE, 'p: null); // 2');
  }

  @failingTest
  Future<void> test_path_inConstructor_named() async {
    // The path does not contain the first expected element.
    addTestFile('''
library my_lib;
class A {}
class B {
  B.named() {
    A a = null;
  }
}
''');
    await findElementReferences(search: 'A {}', false);
    assertHasResult(SearchResultKind.REFERENCE, 'A a = null;');
    expect(getPathString(result.path), '''
LOCAL_VARIABLE a
CONSTRUCTOR named
CLASS B
COMPILATION_UNIT test.dart
LIBRARY my_lib''');
  }

  @failingTest
  Future<void> test_path_inConstructor_unnamed() async {
    // The path does not contain the first expected element.
    addTestFile('''
library my_lib;
class A {}
class B {
  B() {
    A a = null;
  }
}
''');
    await findElementReferences(search: 'A {}', false);
    assertHasResult(SearchResultKind.REFERENCE, 'A a = null;');
    expect(getPathString(result.path), '''
LOCAL_VARIABLE a
CONSTRUCTOR
CLASS B
COMPILATION_UNIT test.dart
LIBRARY my_lib''');
  }

  Future<void> test_path_inExtension_named() async {
    // This test fails when run on macOS with a Windows pathContext because
    // analysis_server/lib/plugin/protocol/protocol_dart.dart
    // getElementDisplayName uses path.basename() without any path context.
    addTestFile('''
class A {
  void foo() {}
}

extension E on A {
  void bar() {
    foo();
  }
}
''');
    await findElementReferences(search: 'foo() {}', false);
    assertHasResult(SearchResultKind.INVOCATION, 'foo();');
    expect(getPathString(result.path), '''
METHOD bar
EXTENSION E
COMPILATION_UNIT test.dart
LIBRARY''');
  }

  Future<void> test_path_inExtension_unnamed() async {
    // This test fails when run on macOS with a Windows pathContext because
    // analysis_server/lib/plugin/protocol/protocol_dart.dart
    // getElementDisplayName uses path.basename() without any path context.
    addTestFile('''
class A {
  void foo() {}
}

extension on A {
  void bar() {
    foo();
  }
}
''');
    await findElementReferences(search: 'foo() {}', false);
    assertHasResult(SearchResultKind.INVOCATION, 'foo();');
    expect(getPathString(result.path), '''
METHOD bar
EXTENSION
COMPILATION_UNIT test.dart
LIBRARY''');
  }

  @failingTest
  Future<void> test_path_inFunction() async {
    // The path does not contain the first expected element.
    addTestFile('''
library my_lib;
class A {}
void f() {
  A a = null;
}
''');
    await findElementReferences(search: 'A {}', false);
    assertHasResult(SearchResultKind.REFERENCE, 'A a = null;');
    expect(getPathString(result.path), '''
LOCAL_VARIABLE a
FUNCTION f
COMPILATION_UNIT test.dart
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.REFERENCE},
      '''
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.REFERENCE},
      '''
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.REFERENCE},
      '''
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 setPriorityFiles2([testFile]);
    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 {
  test(p) {}
}
void f(A a, p) {
  a.test(1);
  p.test(2);
}
''');
    await findElementReferences(search: 'test(p) {}', false);
    assertHasResult(SearchResultKind.INVOCATION, 'test(1);');
    assertNoResult(SearchResultKind.INVOCATION, 'test(2);');
  }

  Future<void> test_potential_field() async {
    addTestFile('''
class A {
  var test; // declaration
}
void f(A a, p) {
  a.test = 1;
  p.test = 2;
  print(p.test); // p
}
''');
    await findElementReferences(search: 'test; // declaration', true);
    {
      assertHasResult(SearchResultKind.WRITE, 'test = 1;');
      expect(result.isPotential, isFalse);
    }
    {
      assertHasResult(SearchResultKind.WRITE, 'test = 2;');
      expect(result.isPotential, isTrue);
    }
    {
      assertHasResult(SearchResultKind.READ, 'test); // p');
      expect(result.isPotential, isTrue);
    }
  }

  Future<void> test_potential_method() async {
    addTestFile('''
class A {
  test(p) {}
}
void f(A a, p) {
  a.test(1);
  p.test(2);
}
''');
    await findElementReferences(search: 'test(p) {}', true);
    {
      assertHasResult(SearchResultKind.INVOCATION, 'test(1);');
      expect(result.isPotential, isFalse);
    }
    {
      assertHasResult(SearchResultKind.INVOCATION, 'test(2);');
      expect(result.isPotential, isTrue);
    }
  }

  Future<void> test_potential_method_definedInSubclass() async {
    addTestFile('''
class Base {
  methodInBase() {
    test(1);
  }
}
class Derived extends Base {
  test(_) {} // of Derived
  methodInDerived() {
    test(2);
  }
}
globalFunction(Base b) {
  b.test(3);
}
''');
    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);
  }

  Future<void> test_prefix() async {
    addTestFile('''
import 'dart:async' as ppp;
void f() {
  ppp.Future a;
  ppp.Stream b;
}
''');
    await findElementReferences(search: 'ppp;', false);
    var searchElement = this.searchElement!;
    expect(searchElement.kind, ElementKind.PREFIX);
    expect(searchElement.name, 'ppp');
    expect(searchElement.location!.startLine, 1);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'ppp.Future');
    assertHasResult(SearchResultKind.REFERENCE, 'ppp.Stream');
  }

  Future<void> test_topFunction_parameter_optionalNamed_anywhere() async {
    addTestFile('''
void foo(int a, int b, {int? test}) {
  test;
}

void g() {
  foo(0, test: 2, 1);
}
''');
    await findElementReferences(search: 'test})', false);
    expect(searchElement!.kind, ElementKind.PARAMETER);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.READ, 'test;');
    assertHasResult(SearchResultKind.REFERENCE, 'test: 2');
  }

  Future<void> test_topLevelVariable_explicit() async {
    addTestFile('''
var vvv = 1;
void f() {
  print(vvv);
  vvv += 3;
  vvv = 2;
  vvv();
}
''');
    await findElementReferences(search: 'vvv = 1', false);
    expect(searchElement!.kind, ElementKind.TOP_LEVEL_VARIABLE);
    expect(results, hasLength(4));
    assertHasResult(SearchResultKind.READ, 'vvv);');
    assertHasResult(SearchResultKind.WRITE, 'vvv += 3');
    assertHasResult(SearchResultKind.WRITE, 'vvv = 2');
    assertHasResult(SearchResultKind.READ, 'vvv();');
  }

  Future<void> test_topLevelVariable_implicit() async {
    addTestFile('''
get vvv => null;
set vvv(x) {}
void f() {
  print(vvv);
  vvv = 1;
}
''');
    {
      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(search: 'vvv(x) {}', false);
      expect(results, hasLength(2));
      assertHasResult(SearchResultKind.READ, 'vvv);');
      assertHasResult(SearchResultKind.WRITE, 'vvv = 1;');
    }
  }

  Future<void> test_typeReference_class() async {
    addTestFile('''
void f() {
  int a = 1;
  int b = 2;
}
''');
    await findElementReferences(search: 'int a', false);
    expect(searchElement!.kind, ElementKind.CLASS);
    assertHasResult(SearchResultKind.REFERENCE, 'int a');
    assertHasResult(SearchResultKind.REFERENCE, 'int b');
  }

  Future<void> test_typeReference_extensionType() async {
    addTestFile('''
extension type A(int it) {}

extension type B(int it) implements A {}

void f(A a) {}
''');
    await findElementReferences(search: 'A(int it)', false);
    expect(searchElement!.kind, ElementKind.EXTENSION_TYPE);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'A {}');
    assertHasResult(SearchResultKind.REFERENCE, 'A a) {}');
  }

  Future<void> test_typeReference_typeAlias_functionType() async {
    addTestFile('''
typedef F = Function();
void f(F f) {
}
''');
    await findElementReferences(search: 'F =', false);
    expect(searchElement!.kind, ElementKind.TYPE_ALIAS);
    expect(results, hasLength(1));
    assertHasResult(SearchResultKind.REFERENCE, 'F f');
  }

  Future<void> test_typeReference_typeAlias_interfaceType() async {
    addTestFile('''
typedef A<T> = Map<int, T>;

void(A<String> a) {}
''');
    // Can find `A`.
    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(search: 'int,', false);
    expect(searchElement!.kind, ElementKind.CLASS);
    assertHasResult(SearchResultKind.REFERENCE, 'int,');
  }

  Future<void> test_typeReference_typeAlias_legacy() async {
    addTestFile('''
typedef F();
void f(F f) {
}
''');
    await findElementReferences(search: 'F()', false);
    expect(searchElement!.kind, ElementKind.TYPE_ALIAS);
    expect(results, hasLength(1));
    assertHasResult(SearchResultKind.REFERENCE, 'F f');
  }

  Future<void> test_typeReference_typeVariable() async {
    addTestFile('''
class A<T> {
  T f;
  T m() => null;
}
''');
    await findElementReferences(search: 'T> {', false);
    expect(searchElement!.kind, ElementKind.TYPE_PARAMETER);
    expect(results, hasLength(2));
    assertHasResult(SearchResultKind.REFERENCE, 'T f;');
    assertHasResult(SearchResultKind.REFERENCE, 'T m()');
  }

  Future<void> test_variable_forEachElement_block() async {
    await setPriorityFiles2([testFile]);
    addTestFile('''
void f(List<int> values) {
  {
    [for (final value in values) value * 2];
  }
}
''');
    await findElementReferences(search: 'value in', false);
    expect(searchElement!.kind, ElementKind.LOCAL_VARIABLE);
    assertHasResult(SearchResultKind.READ, 'value * 2');
  }

  Future<void> test_variable_forEachElement_expressionBody() async {
    await setPriorityFiles2([testFile]);
    addTestFile('''
Object f() => [for (final value in []) value * 2];
''');
    await findElementReferences(search: 'value in', false);
    expect(searchElement!.kind, ElementKind.LOCAL_VARIABLE);
    assertHasResult(SearchResultKind.READ, 'value * 2');
  }

  Future<void> test_variable_forEachElement_functionBody() async {
    await setPriorityFiles2([testFile]);
    addTestFile('''
void f(List<int> values) {
  [for (final value in values) value * 2];
}
''');
    await findElementReferences(search: 'value in', false);
    expect(searchElement!.kind, ElementKind.LOCAL_VARIABLE);
    assertHasResult(SearchResultKind.READ, 'value * 2');
  }

  Future<void> test_variable_forEachElement_topLevelVariable() async {
    await setPriorityFiles2([testFile]);
    addTestFile('''
final a = [for (final value in []) value * 2];
''');
    await findElementReferences(search: 'value in', false);
    expect(searchElement!.kind, ElementKind.LOCAL_VARIABLE);
    assertHasResult(SearchResultKind.READ, 'value * 2');
  }
}
