blob: 5fb68d67f8f0494deec537012f86b95702a83776 [file] [log] [blame]
// 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.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);
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.READ, 'foo: 0');
assertHasResult(SearchResultKind.READ, ': 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.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 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');
}
}